The problem is that with Camera pictures, you can't predict what physical image size (pixel width x height) will correspond to a certain size in bytes.
If you have a hard, fixed limit on the size (in bytes) that you can upload, you might need to do something like this:
experiment with a few images, and find an approximate image size (width x height) that will produce a JPG file that normally fits within your 400-500KB limit
in your app, resize the Camera images to that physical size (width x height, in pixels)
check the size of the new JPG data, and see if it fits under your limit
if it does not fit, then you'll have to rescale the original image to a smaller size
As you can see, this isn't that simple to do. Most servers that I've seen (e.g. Facebook) tell you the maximum physical size in pixels that your image can be (e.g. 960 pixels as the widest size ... either width or height). If that's good enough for your server, it's much easier to code on the BlackBerry client side.
Restrict to a Fixed Pixel Width and Height
You could use something like this:
FileConnection file;
InputStream inputStream;
try {
file = (FileConnection) Connector.open(url); // JPG file:// URL
if (file.exists())
{
inputStream = file.openInputStream();
byte[] data = IOUtilities.streamToBytes(inputStream);
Bitmap original = Bitmap.createBitmapFromBytes(data, 0, data.length, 1);
Bitmap scaledImg = new Bitmap(640, 480); // maximum width and height
original.scaleInto(scaledImg,
Bitmap.FILTER_LANCZOS, /* LANCZOS is for best quality */
Bitmap.SCALE_TO_FIT);
// http://stackoverflow.com/a/14147236/119114
int jpegQuality = 85;
EncodedImage encodedImg = JPEGEncodedImage.encode(scaledImg, jpegQuality);
byte[] imageData = encodedImg.getData();
// TODO: send imageData as you already were
}
} catch (Exception e) {
// log exception
} finally {
try {
if (file != null) {
file.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException ioe) {
// nothing can be done here
}
}
Of course, you should perform all this work on a background thread. Once you know the final image size, if you really want to, you can notify the user with something like:
final uploadSizeKb = imageData.length / 1024;
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert(uploadSizeKb + "KB uploaded to server");
}
});
Further Optimizations
As you can probably tell, there's things you can adjust with this algorithm:
You could optimize by checking to see if the image file is already small enough, before trying to scale it. (check
file.fileSize()
)You could speed up the image scaling by using
Bitmap.FILTER_BILINEAR
orBitmap.FILTER_BOX
instead ofBitmap.FILTER_LANCZOS
.You can change the JPEG quality factor from 85 when you convert back to JPEG for uploading
You might need to check image orientation to avoid wasting too much space when you scale with
SCALE_TO_FIT
. If the Camera image is in the wrong orientation, just switch thescaledImg
bitmap width and height (e.g. 640x480 -> 480x640)You actually can skip a couple steps, and scale directly when reading in the image file, with
createBitmapFromBytes()
. The last parameter is a scale parameter. Unfortunately, since photographs are all different, it would also be difficult to pick one scale ratio that would work. As I said, it's more common that the server simply specifies a maximum image size, in pixels.
OS < 5.0 Support
If you don't have the image scaling APIs in OS 5.0 available, this older toolkit can be useful.