/*
* Geopaparazzi - Digital field mapping on Android based devices
* Copyright (C) 2016 HydroloGIS (www.hydrologis.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.geopaparazzi.library.images;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import eu.geopaparazzi.library.util.TimeUtilities;
/**
* Images helper utilities.
*
* @author Andrea Antonello (www.hydrologis.com)
*/
public class ImageUtilities {
public static final int MAXBLOBSIZE = 1900000;
public static final int THUMBNAILWIDTH = 100;
public static String getSketchImageName(Date date) {
if (date == null)
date = new Date();
String currentDatestring = TimeUtilities.INSTANCE.TIMESTAMPFORMATTER_UTC.format(date);
return "SKETCH_" + currentDatestring + ".png";
}
public static String getCameraImageName(Date date) {
if (date == null)
date = new Date();
String currentDatestring = TimeUtilities.INSTANCE.TIMESTAMPFORMATTER_UTC.format(date);
return "IMG_" + currentDatestring + ".jpg";
}
public static String getMapImageName(Date date) {
if (date == null)
date = new Date();
String currentDatestring = TimeUtilities.INSTANCE.TIMESTAMPFORMATTER_UTC.format(date);
return "MAP_" + currentDatestring + ".png";
}
public static boolean isImagePath(String path) {
return path.toLowerCase().endsWith("jpg") || path.toLowerCase().endsWith("png");
}
/**
* Get the default temporary image file name.
*
* @param ext and optional dot+extension to add. If null, '.jpg' is used.
* @return the image name.
*/
public static String getTempImageName(String ext) {
if (ext == null) ext = ".jpg";
return "tmp_gp_image" + ext;
}
/**
* Get an image from a file by its path.
*
* @param imageFilePath the image path.
* @param tryCount times to try in 300 millis loop, in case the image is
* not yet on disk. (ugly but no other way right now)
* @return the image data or null.
*/
public static byte[] getImageFromPath(String imageFilePath, int tryCount) {
Bitmap image = BitmapFactory.decodeFile(imageFilePath);
int count = 0;
while (image == null && ++count < tryCount) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
image = BitmapFactory.decodeFile(imageFilePath);
}
if (image == null) return null;
// It is necessary to rotate the image before converting to bytes, as the exif information
// will be lost afterwards and the image will be incorrectly oriented in some devices
float orientation = getRotation(imageFilePath);
if (orientation > 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
image = Bitmap.createBitmap(image, 0, 0, image.getWidth(),
image.getHeight(), matrix, true);
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 90, stream);
return stream.toByteArray();
}
/**
* Get an image and thumbnail from a file by its path.
*
* @param imageFilePath the image path.
* @param tryCount times to try in 300 millis loop, in case the image is
* not yet on disk. (ugly but no other way right now)
* @return the image and thumbnail data or null.
*/
public static byte[][] getImageAndThumbnailFromPath(String imageFilePath, int tryCount) {
byte[][] imageAndThumbNail = new byte[2][];
// first read full image and check existence
Bitmap image = BitmapFactory.decodeFile(imageFilePath);
int count = 0;
while (image == null && ++count < tryCount) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
image = BitmapFactory.decodeFile(imageFilePath);
}
if (image == null) return null;
// It is necessary to rotate the image before converting to bytes, as the exif information
// will be lost afterwards and the image will be incorrectly oriented in some devices
float orientation = getRotation(imageFilePath);
if (orientation > 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
image = Bitmap.createBitmap(image, 0, 0, image.getWidth(),
image.getHeight(), matrix, true);
}
int width = image.getWidth();
int height = image.getHeight();
// define sampling for thumbnail
float sampleSizeF = (float) width / (float) THUMBNAILWIDTH;
float newHeight = height/sampleSizeF;
Bitmap thumbnail = Bitmap.createScaledBitmap(image, THUMBNAILWIDTH, (int)newHeight, false);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 90, stream);
byte[] imageBytes = stream.toByteArray();
stream = new ByteArrayOutputStream();
thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, stream);
byte[] thumbnailBytes = stream.toByteArray();
imageAndThumbNail[0] = imageBytes;
imageAndThumbNail[1] = thumbnailBytes;
return imageAndThumbNail;
}
public static Bitmap getScaledBitmap(int targetW, int targetH, String imagePath) {
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
return bitmap;
}
public static Bitmap getImageFromImageData(byte[] imageData) {
Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
return bitmap;
}
/**
* Write am image to disk.
*
* @param imageData the data to write.
* @param imagePath the path to write to.
* @throws IOException
*/
public static void writeImageDataToFile(byte[] imageData, String imagePath) throws IOException {
FileOutputStream fout = new FileOutputStream(imagePath);
try {
fout.write(imageData);
} finally {
fout.close();
}
}
/**
* Calculates the optimum inSampleSize parameter based on the real
* image size and the required subsampled size
*
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
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;
}
/**
* Loads a subsampled version of the image
*
* @param imgPath The path to the image to load
* @param reqWidth The width required for the subsampled version
* @param reqHeight The height required for the subsampled version
* @return
*/
public static Bitmap decodeSampledBitmapFromFile(String imgPath,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgPath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imgPath, options);
}
public static float getRotation(String imagePath) {
try {
ExifInterface exif = new ExifInterface(imagePath);
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90f;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180f;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270f;
}
} catch (IOException e) {}
return 0f;
}
}