UPDATE 01/16/2018
The simplest approach is to use BitmapFactory.Options.inSampleSize
to decode the byte array and compress it at the same time. Here is the doc
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4; // If you want an image four times smaller than the original
Bitmap decoded = BitmapFactory.decodeByteArray(data, 0, data.length, options);
OLD ANSWER, PLEASE DON'T USE THIS
Since there is apparently no way to achieve this in one shot, I implemented an iterative process to reach a given size in KB.
I start with a compression coefficient equal to 80, and if it is not enough I decrease this coefficient and I retry until I get a size below my threshold.
static COMPRESSION_PERCENTAGE_START = 80;
static IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS = 3;
static IMAGE_COMPRESSION_STEP_PERCENT = 5;
// For logging
static IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS = 5;
static byte[] compressCapture(byte[] capture, int maxSizeKB) {
long maxSizeByte = ((long) maxSizeKB) * 1_000;
if (capture.length <= maxSizeByte) return capture;
byte[] compressed = capture;
// Chosen arbitrarily so that we can compress multiple times to reach the expected size.
int compression = COMPRESSION_PERCENTAGE_START;
Bitmap bitmap = BitmapFactory.decodeByteArray(capture, 0, capture.length);
ByteArrayOutputStream outputStream;
int iterations = 0;
while (compressed.length > maxSizeByte) {
// Just a counter
iterations++;
compression -= IMAGE_COMPRESSION_STEP_PERCENT;
outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, compression, outputStream);
compressed = outputStream.toByteArray();
}
if (iterations > IMAGE_COMPRESSION_EXPECTED_MAX_ITERATIONS) {
Timber.w("Compression process has iterated more than expected, with " + iterations + " iterations.");
}
return compressed;
}
Here is the output size for an original size of 1_871_058 bytes
- Iteration #1 (compression equal to 80) : 528_593 bytes
- Iteration #2 (compression equal to 75) : 456_591 bytes
It does the job for me but please be careful if you use that, it certainly needs some fine tuning and I just tested it on a given smartphone model.