I want to make Video Collage in which 2 or more videos should be displayed in one frame and then they can be converted into one Video file.
I tried examples but they just add videos at the end of each video to make a long one combine video.
Any Help Please
String FILE_PATH = "/storage/sdcard0/testing.mp4";
String FILE_PATH2 = "/storage/sdcard0/testing1.mp4";
String FILE_PATH3 = "/storage/sdcard0/testing2.mp4";
File file1 = new File(FILE_PATH);
File file2 = new File(FILE_PATH2);
File file3 = new File(FILE_PATH3);
private ProgressDialog pDialog;
ImageView img,img2,img3;
MediaMetadataRetriever retriever2 = new MediaMetadataRetriever();
MediaMetadataRetriever retriever3 = new MediaMetadataRetriever();
ArrayList<Bitmap> bitmapArray1 = new ArrayList<Bitmap>();
ArrayList<Bitmap> bitmapArray2 = new ArrayList<Bitmap>();
ArrayList<Bitmap> bitmapArray3 = new ArrayList<Bitmap>();
File ScreenDIR = new File("/sdcard/Screens/");
// have the object build the directory structure, if needed.
double id1=0,id2=0,id3=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ScreenDIR.mkdirs();
img = (ImageView)findViewById(R.id.imageView);
img2 = (ImageView)findViewById(R.id.imageView2);
img3 = (ImageView)findViewById(R.id.imageView3);
new LoadAllProducts().execute();
}
class LoadAllProducts extends AsyncTask<String, String, String> {
/**
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(MainActivity.this);
pDialog.setMessage("Extracting Frames. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args) {
if(file1.exists()){
for (long i = 0; i < 5000; i += 1000/14) { // lenms - video length in milliseconds
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(file1.toString());
// Bitmap bitmap = retriever.getFrameAtTime((i*1000/14), MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
saveBitmapToCahche( getResizedBitmap((retriever.getFrameAtTime((i*1000/14), MediaMetadataRetriever.OPTION_CLOSEST_SYNC)), 500) ,String.valueOf(id1));
id1++;
//bitmapArray1.add(bitmap);
/* File file = new File(ScreenDIR, "sketchpad1" + id1 + ".png");
FileOutputStream fOut = null;
try {
fOut = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//bitmap.compress(Bitmap.CompressFormat.PNG, 30, fOut);
try {
fOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}*/
}
}
/* if(file2.exists()){
retriever2.setDataSource(file2.toString());
for (long i = 0; i < 3000; i += 1000/24) { // lenms - video length in milliseconds
bitmap2 = retriever2.getFrameAtTime(i*1000/29, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
//bitmapArray2.add(bitmap2);
File file = new File(ScreenDIR, "sketchpad2" + id2 + ".png");
FileOutputStream fOut = null;
try {
fOut = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
bitmap2.compress(Bitmap.CompressFormat.PNG, 85, fOut);
try {
fOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
fOut.close();
id2++;
} catch (IOException e) {
e.printStackTrace();
}
}
}
if(file3.exists()){
retriever3.setDataSource(file3.toString());
for (long i = 0; i < 3000; i += 1000/24) { // lenms - video length in milliseconds
bitmap3 = retriever3.getFrameAtTime(i*1000/29, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
// bitmapArray3.add(bitmap3);
File file = new File(ScreenDIR, "sketchpad3" + id3 + ".png");
FileOutputStream fOut = null;
try {
fOut = new FileOutputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
bitmap3.compress(Bitmap.CompressFormat.PNG, 85, fOut);
try {
fOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
fOut.close();
id3++;
} catch (IOException e) {
e.printStackTrace();
}
}
}*/
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
// dismiss the dialog after getting all products
pDialog.dismiss();
img.setImageBitmap(retrieveBitmapFromCache(String.valueOf(id2)));
id2 = 50;
img2.setImageBitmap(retrieveBitmapFromCache(String.valueOf(id2)));
id2 = 69;
img3.setImageBitmap(retrieveBitmapFromCache(String.valueOf(id2)));
// img2.setImageBitmap(bitmapArray2.get(0));
// img3.setImageBitmap(bitmapArray3.get(0));
}
}
public void saveBitmapToCahche(Bitmap bb,String ID ){
Cache.getInstance().getLru().put(ID, bb);
}
public Bitmap retrieveBitmapFromCache(String ID) {
Bitmap bitmap = (Bitmap) Cache.getInstance().getLru().get(ID);
return bitmap;
}
public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
int width = image.getWidth();
int height = image.getHeight();
float bitmapRatio = (float)width / (float) height;
if (bitmapRatio > 0) {
width = maxSize;
height = (int) (width / bitmapRatio);
} else {
height = maxSize;
width = (int) (height * bitmapRatio);
}
return Bitmap.createScaledBitmap(image, width, height, true);
}
}
`
Creating a bitmap for each frame of video is terribly slow and not storage friendly at all. besides that MediaMetadataRetriever will give you lots of redundant frames. when you are done with creating bitmaps for both videos you will only have 4-5 different bitmaps from both videos.
To extract each and every frame from videos you will have to use MediaCodec with MediaExtractor.
Creating a collage is bit tricky. one thing is H264 coded only supports certain frame sizes. when you add two video side by side it wont necessarily fit into supported frame sizes.
That said, i thinks its possible with rendering frames side by side from both videos onto GLSurfaceView and sharing that surface to Mediacodec which will encode that frames to h264. i have not implemented this but i believe this is one way to do it without dealing with NDK.
FFMPEG also has in built functionality for this. if you are comfortable with NDK.
Split video into frames, e.g.
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
File file1 = new File(directory, "file.mp4");
retriever.setDataSource(file1.toString());
for (long i = 0; i < lenms; i += 1000/fps) { // lenms - video length in milliseconds
Bitmap bitmap = retriever.getFrameAtTime(i*1000/fps, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
}
and make a new frames from bitmap
Related
I am using the following code to download an image from an url, then saving to sqlite and then view in imageview in an activity.
new LoadProfileImage().execute(jsonObject.getString("image"), id, title, promoexpdate, String.valueOf(i),flag,promostartDate);
The above code is used to call the function to do the above work.
private class LoadProfileImage extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
String x,y,z,a,w,s;
protected Bitmap doInBackground(String... uri) {
String url = uri[0];
Log.d("ImageURL",url);
x = uri[1];
y = uri[2];
z = uri[3];
a = uri[4];
w = uri[5];
s = uri[6];
Log.d("LogValue",url+x+y+z+a+w+s);
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(url).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (IOException e) {
Log.e("ErroronImageParsing", e.getLocalizedMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
if (result != null) {
int width = result.getWidth();
int height = result.getHeight();
Bitmap newBitmap = Bitmap.createScaledBitmap(result, width / 2, height / 2, true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
newBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
buffer = out.toByteArray();
if (result!= newBitmap){
result.recycle();
}
Log.d("ImageUploaded", "Success");
}
try {
dbManager.open();
Cursor cursor = dbManager.fetch_PromsID(x);
if (cursor.getCount() > 0){
String fla = cursor.getString(cursor.getColumnIndex(DatabaseHelper.PRO_FLAG));
String pri_ID = cursor.getString(cursor.getColumnIndex(DatabaseHelper.PRO_ID));
if (!w.equals(fla)) {
dbManager.update_Promotions(pri_ID,y,z, buffer,w,s);
}
}else {
dbManager.insertPromotions(x,y,z,buffer,w,s);
}
} catch (SQLException e) {
e.printStackTrace();
}
SqliteData();
panel.setVisibility(View.GONE);
dbManager.close();
}
}
Here, when the code below is executed, image from the url is saved into internal storage. I wish to disable the auto saving while maintaining my intention. Thanks in advance...
Bitmap newBitmap = Bitmap.createScaledBitmap(result, width / 2, height / 2, true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
newBitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
buffer = out.toByteArray();
Try to use third library like Picasso or Glid
that offer
loading without caching
loading with memory or storage caching
you can do it with single line of code
Picasso.with(context).load(imageUrl)
.error(R.drawable.error)
.placeholder(R.drawable.placeholder)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.into(imageView);
My app displays a picture and text, when my user clicks on said picture I wish to enlarge it.
So far I do it through an Activity that displays a Bitmap:
// ImageView containing the image.
final ImageView apd = (ImageView) view.findViewById(R.id.apd_image);
apd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Save ImageView as Bitmap
apd.setDrawingCacheEnabled(true);
apd.buildDrawingCache(true);
Bitmap bitmap = apd.getDrawingCache();
// Create intent and set image to display.
Intent intent = new Intent(getActivity(), GalleryDetailsActivity.class);
intent.putExtra("image", bitmap);
//Start details activity
startActivity(intent);
apd.destroyDrawingCache();
}
});
Here is the Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_details_activity);
Bitmap bitmap = getIntent().getParcelableExtra("image");
ImageView imageView = (ImageView) findViewById(R.id.image);
imageView.setImageBitmap(bitmap);
}
But when I click on my picture I receive the following error message in the debugger:
E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!
Some research got me to reduce the image quality in order to display it like such:
final float densityMultiplier = getResources().getDisplayMetrics().density;
int h = (int) (50 * densityMultiplier);
int w = (int) (h * bitmap.getWidth() / ((double) bitmap.getHeight()));
bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
It works, but it's not what I want.
Would there be a way to widen my image and keep its quality?
EDIT:
As recommended I now Save my picture in my file system like such:
private void savePicture(String filename, Bitmap b, Context ctx){
try {
ObjectOutputStream oos;
FileOutputStream out;// = new FileOutputStream(filename);
out = ctx.openFileOutput(filename, Context.MODE_PRIVATE);
oos = new ObjectOutputStream(out);
b.compress(Bitmap.CompressFormat.PNG, 100, oos);
oos.close();
oos.notifyAll();
out.notifyAll();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
And load it in the activity like such:
private Bitmap loadPicture(String filename) {
Bitmap b = null;
try {
FileInputStream fis = openFileInput(filename);
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(fis);
} catch (IOException e1) {
e1.printStackTrace();
}
b = BitmapFactory.decodeStream(ois);
try {
ois.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return b;
}
I now want to display it in a bigger size.
Don't pass the image via intent. Intents are meant to hold a small amount of data, not images. Write it to the file system, and pass the file name via intent.
I'm having quite a problem here.
When I read image and then save it, I get the same picture. But when I open the pixel value, the value of each pixel is slightly different(larger or smaller around 10 units).
Why did that pixel change? I only read the image, then save it, I don't make changes to the pixel. I create it with format RGB and save as a PNG with ByteArrayOutputStream method.
private void onCaptureImageResult(Intent data) {
Bitmap thumbnail = (Bitmap) data.getExtras().get("data");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, bytes);
File destination = new File(Environment.getExternalStorageDirectory(),
System.currentTimeMillis() + ".jpg");
FileOutputStream fo;
try {
destination.createNewFile();
fo = new FileOutputStream(destination);
fo.write(bytes.toByteArray());
fo.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
ivImage.setImageBitmap(thumbnail);
}
private void onSelectFromGalleryResult(Intent data) {
Bitmap bm=null;
if (data != null) {
try {
bm = MediaStore.Images.Media.getBitmap(getApplicationContext().getContentResolver(), data.getData());
} catch (IOException e) {
e.printStackTrace();
}
}
bmp = bm;
}
public void save(View view){
operation= Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(),bmp.getConfig());
int size = bmp.getRowBytes() * bmp.getHeight();
bytearrayoutputstream = new ByteArrayOutputStream();
int[] gambarR = new int[size];
int[] gambarG = new int[size];
int[] gambarB = new int[size];
int[] gambarA = new int[size];
int k = 0;
for(int i=0; i<bmp.getWidth(); i++){
for(int j=0; j<bmp.getHeight(); j++){
int p = bmp.getPixel(i, j);
gambarR[k] = Color.red(p);
gambarG[k] = Color.green(p);
gambarB[k] = Color.blue(p);
gambarA[k] = Color.alpha(p);
k++;
}
}
int l = 0;
for(int i = 0; i<bmp.getWidth(); i++){
for(int j = 0; j<bmp.getHeight();j++){
operation.setPixel(i, j, Color.rgb(gambarR[l], gambarG[l], gambarB[l]));
l++;
}
}
String fileName = "_hasil.bmp";
Long tsLong = System.currentTimeMillis()/1000;
String ts = tsLong.toString();
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
File gambar = new File(baseDir + File.separator + ts + fileName);
try
{
gambar.createNewFile();
fileoutputstream = new FileOutputStream(gambar);
fileoutputstream.write(bytearrayoutputstream.toByteArray());
fileoutputstream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
ivImage.setImageBitmap(operation);
}
I will show you the difference between the image. I only read and save, and don't change the pixel. I need the pixel didn't change when I save it back.
As others have noticed, much of the code that you posted appears to do not much useful, indicating that you either haven't read the documentation, or haven't thought through the problem thoroughly.
However, the specific problem appears to be that you are saving your image in a lossy compression format (JPEG), in this case, at 90% quality. "Lossy" means that by definition you will never get back exactly the bitmap that you had before compression. Even setting JPEG quality to 100% is unlikely to get you exactly the same bitmap as before compression.
If you want exactly the same values back when reading the file, you'll need to write a lossless format, such as PNG or BMP.
In my app I have to upload selected images to parse.com for taking their Printout . I have to maintain image quality and I could not resize the images.
I have to upload images in the parse.com ..I do not need to show them on device screen (images are form image gallery or from facebook album..or from sdcard) . I could not scale down them as per requirement.
I am getting OutOfMemory error on BitmapFactory.decodeFile(). How to solve this bug ?
is using android:largeHeap="true" could sove my issue ?
I am getting this crash on Samsung SM-G900T, But not on emulator ..
I tried to put
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565;
But it is not working.
Below is my AsyncTask class for uploading images to Parse.com
class UploadFileFromURL extends AsyncTask<String, String, String> {
ProgressDialog dialog;
String albumId = "";
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... f_url) {
try {
for (int i = 0; i < arrListImgBean.size(); i++) {
if (!isUploading || objAsyncUpload.isCancelled()) {
break;
}
try {
if (arrListImgBean.get(i).imageStatus == 1)
continue;
else if (arrListImgBean.get(i).imageStatus == 2) {
isPhotodeleted = true;
publishProgress("" + countUploaded);
deletePhoto(i);
}
else {
isPhotodeleted = false;
try {
Bitmap b = null;
InputStream is = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inPreferredConfig = Config.RGB_565; // to
// reduce
// the
// memory
options.inDither = true;
if (arrListImgBean.get(i).imgURL
.startsWith("http")) {
try {
URL url = new URL(
arrListImgBean.get(i).imgURL);
is = url.openConnection()
.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b = BitmapFactory.decodeStream(is, null,
options);
} else {
b = BitmapFactory.decodeFile(
arrListImgBean.get(i).imgURL,
options);
}
// Convert it to byte
ByteArrayOutputStream stream = new ByteArrayOutputStream();
// Bitmap out = Bitmap.createScaledBitmap(b,
// 1500, 2100, false);
b.compress(Bitmap.CompressFormat.PNG, 100,
stream);
byte[] image = stream.toByteArray();
ParseFile file = new ParseFile("Android.png",
image);
file.save();
String uploadedUrl = file.getUrl();
if (uploadedUrl != null) {
ParseObject imgupload = new ParseObject(
"Photo");
imgupload.put("userName", ParseUser
.getCurrentUser().getEmail());
imgupload.put("photoURL", file);
imgupload.put("photoID",
arrListImgBean.get(i).imageId);
imgupload.put("count", 1);
imgupload.put("albumName", albumId);
imgupload.save();
String objId = imgupload.getObjectId();
if (objId != null && !objId.isEmpty()) {
countUploaded++;
publishProgress("" + countUploaded);
database.updateImageStatus(
arrListImgBean.get(i).imageId,
Constants.STATUS_UPLOADED,
objId, uploadedUrl);
}
}
} catch (Exception e) {
}
}
} catch (Exception e) {
isUploading = false;
e.printStackTrace();
}
}
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(String file_url) {
// dismissDialog(progress_bar_type);
isUploading = false;
btnUploadImages.setBackgroundResource(R.drawable.upload_photo);
vprogress.setCompoundDrawables(null, null, null, null);
// stopLoading();
setProgressMsg();
}
}
android:largeHeap="true"
This line of code can solve your problem but its a temporary solution but crash may occurs again if number of images or the size of images will increase. Better to Use Picasso library to deals with Images
Consider you have an image of 1024x1024dp and a device with 512x512dp (both figures are just for understanding). So, in this case, loading a full resolution image on a smaller scale device is waste of memory. What you can do is to scale down the image so that it fits the device screen. In this way not only you will save a lot of memory but also get a proper, clear and sharp image.
I am adding code for scaling the image which I am using currently in my project.
final FileInputStream streamIn = new FileInputStream(file);
final BitmapFactory.Options ops = new BitmapFactory.Options();
ops.inJustDecodeBounds = true;
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 300;
int width_tmp = ops.outWidth, height_tmp = ops.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE) {
break;
}
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
ops.inJustDecodeBounds = false;
ops.inSampleSize = scale;
bitmap = BitmapFactory.decodeStream(streamIn, null, ops); //This gets the image
streamIn.close();
Choose a REQUIRED_SIZE value depending on the device's screen display size.
try {
image = readInFile(path);
}
catch(Exception e) {
e.printStackTrace();
}
// Create the ParseFile
ParseFile file = new ParseFile("picturePath", image);
// Upload the image into Parse Cloud
file.saveInBackground();
// Create a New Class called "ImageUpload" in Parse
ParseObject imgupload = new ParseObject("Image");
// Create a column named "ImageName" and set the string
imgupload.put("Image", "picturePath");
// Create a column named "ImageFile" and insert the image
imgupload.put("ImageFile", file);
// Create the class and the columns
imgupload.saveInBackground();
// Show a simple toast message
Toast.makeText(LoadImg.this, "Image Saved, Upload another one ",Toast.LENGTH_SHORT).show();
private byte[] readInFile(String path) throws IOException {
// TODO Auto-generated method stub
byte[] data = null;
File file = new File(path);
InputStream input_stream = new BufferedInputStream(new FileInputStream(
file));
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
data = new byte[16384]; // 16K
int bytes_read;
while ((bytes_read = input_stream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, bytes_read);
}
input_stream.close();
return buffer.toByteArray();
}
I'm having an issue whereby when I write a bitmap to disk, it gets written to disk, however it gets written as a miniscule image (3kb or less in filesize).
I have checked that the source image is indeed the correct dimensions, however the output image seems shrunk despite configuring the bitmap options to not scale.
#Override
protected Void doInBackground(PPImage... params) {
String filename = "pp_" + position + ".jpg";
File externalStorageDirectory = Environment.getExternalStorageDirectory();
final File destination = new File(externalStorageDirectory, filename);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 16;
opts.inPurgeable = true;
opts.inScaled = false;
decode(opts, Uri.parse(params[0].getUri()), getActivity(), new OnBitmapDecodedListener() {
#Override
public void onDecoded(Bitmap bitmap) {
try {
FileOutputStream out = new FileOutputStream(destination, false);
writeImageToFileTask.this.holder.pathToImage = destination.getAbsolutePath();
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
MediaStore.Images.Media.insertImage(getActivity().getContentResolver(), destination.getAbsolutePath(), destination.getName(), destination.getName());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
});
return null;
}
private void decode(BitmapFactory.Options options, Uri mUri, Context mContext, OnBitmapDecodedListener listener) {
try {
InputStream inputStream;
if (mUri.getScheme().startsWith("http") || mUri.getScheme().startsWith("https")) {
inputStream = new URL(mUri.toString()).openStream();
} else {
inputStream = mContext.getContentResolver().openInputStream(mUri);
}
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
listener.onDecoded(bitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
How do I ensure that the image being written to file is the same dimensions as the original source image?
You have specified sample size in your code, which will result in resizing:
opts.inSampleSize = 16;
Just remove this line, and the dimension of the output image should be the same.
About the usage of inSampleSize, according to official doc:
For example, inSampleSize == 4 returns an image that is 1/4 the
width/height of the original, and 1/16 the number of pixels. Any value
<= 1 is treated the same as 1.