I have 4 lines of code to download an Bitmap,
URL u = new URL(webaddress);
InputStream in = null;
in = u.openStream();
icon = BitmapFactory.decodeStream(in);
I'm planning on changing the last line to do something similar to this tutorial where I only load into memory an image of set size to reduce memory usage. However i don't want this to involve another server call/download so I'm curious which of the four lines above actually downloads the data from the source?
Im going to be changing the last line of code to the last two functions in the tutorial mentioned above so could do with knowing if its going to mean downloading more or less data, (I'm trying to only download a small image from one that could be for example 5 megapixels)
Apologies if this is simple / the wrong way to think about it im not very experienced with data streams.
EDIT
im using these two functions to replace the last line of code above:
calling:
image = decodeSampledBitmapFromStram(in, 300,300);
Image quality is not a priority, will this mean more data downloaded?
private static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and
// width
final int heightRatio = Math.round((float) height
/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will
// guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
private Bitmap decodeSampledBitmapFromStream(InputStream in, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Rect padding = new Rect();
BitmapFactory.decodeStream(in, padding, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(in, padding, options);
}
The last of your four lines is responsible for the entire download. BitmapFactory.decodeStream(in); is going to continue pulling data from that stream until either the entire image has been downloaded or an error occurs midway through.
As for the bandwidth issue, I'd be very careful to understand exactly how the decoder downsamples large images before trying things out. In reducing a large image to a smaller size, the only way to do this in a high-quality manner is to downsample by averaging pixels in the original image. If the decoder does downsample in this manner, then you won't save any bandwidth, because the decoder will still need to read every pixel of the original image, even if not every pixel gets stored in RAM. You can downsample more quickly by not reading every pixel in the original image, at the cost of final image quality. Along those lines, I did notice an option for preferring "Quality over Speed":
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inPreferQualityOverSpeed
I have a feeling that, for this particular option, you gain more speed by reading less data, but the API states this only works for JPEGs. Not sure if that helps your particular use case, but it might be worth looking into.
The following document would help you better understand about streaming http://docs.oracle.com/javase/tutorial/essential/io/streams.html. In short, once a connection to the resource location is established, a definite sized buffer (portion of data) is retrieved/read. Typically, this process is continued until all of the portions are read.
The main advantage of streaming is to operate in a piece meal fashion. For example, say if you want to download an image that is 500 MB in size. Rather than transferring this in one shot, streaming allows to download in chunks. This is better in error handling, retries, peak network utilization etc.
Related
This is my use case:
ByteArray ba; // Some value is assigned here
Bitmap bitmap = BitmapFactory.decodeByteArray(ba, 0, ba.length);
Because the ByteArray object is to large, an OutOfMemoryError exception is thrown at the second line, when doing:
BitmapFactory.decodeByteArray(ba, 0, ba.length);
Already tried:
ByteArray ba; // Some value is assigned here
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4; //or whatever value
Bitmap bitmap = BitmapFactory.decodeByteArray(ba, 0, ba.length, options);
The problem with this solution is that, using inSampleSize attribute, it avoids the OutOfMemoryError exception, but the bitmap size (dimensions: width x height) is reduced.
Instead I'm looking for something similar to this:
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream);
At this example the quality of the bitmap is reduced, BUT its size is still the same. When I display it in an ImageView:
iv.setImageBitmap(bitmap);
It occupies the same space as the original, but with half quality.
The problem is, that in my case I cannot use bitmap.compress because my bitmap is null. That is, the compress method can be used after you have a valid Bitmap object, which is not my case.
Question:
Is there any solution using BitmapFactory.Options which can lead to the same result as bitmap.compress: lower quality, same dimensions?
Is there any solution using BitmapFactory.Options which can lead to the same result as bitmap.compress: lower quality, same dimensions?
Not really. A Bitmap is uncompressed by its very nature.
The problem is, that in my case I cannot use bitmap.compress because my bitmap is null.
You are confusing an encoded JPEG image with a Bitmap. An encoded JPEG image is compressed. A Bitmap is not. A Bitmap always consumes memory based on the width, height, and the number of bits per pixel.
You could use a different number of bits per pixel. BitmapFactory uses ARGB_8888 (32 bits/pixel). You could switch to RGB_565 (16 bits/pixel), if your image has no alpha channel and you can live with the reduced range of colors.
Otherwise, your only option is to reduce the size (width and height) of the image.
You cannot compress the bitmap as you want.
You may already know this - But, Yes! you can find the appropriate inSampleSize by this method to maintain the quality based on the size.
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;
}
This method is picked from Android Loading Large images efficiently.
You can read more about handing Bimaps here
Task: I want to resize and move an image across the screen. I want to do this smoothly no matter how big the image is. The code should be supported by the API level 8.
Problem: I tried to use ImageView with scaleType="matrix". Calling ImageView.setMatrix() and then ImageView.invalidate() works great with small images but horrible with big ones. No matter how big the ImageView is.
Can I somehow speed up repainting of the ImageView so it will not recalculate whole image? Maybe there is a way to accomplish the task using different component?
EDIT: More information on what I am trying to achieve.
pw, ph - width and height of the picture (in pixels)
dw, dh - width and height of the device's display (in pixels)
fw, fh - width and height of the visible frame (in pixels)
x, y - position of top left corner of the frame (in pixels)
I want to display a part of the image on the screen. Properties x, y, fw and fh are changing constantly.
I am looking for a part of code (idea), or components which for these 8 specified variables will quickly generate and display the part of the image.
EDIT 2: Info on pw and ph
I assume pw and ph can hold values from 1 to infinity. If this approach causes a lot of trouble we can assume the picture is not bigger than the picture taken with the device's camera.
With your help (community) I figured out the solution.
I am sure that there are other better ways to do it but my solution is not very complicated and should work with any image, any Android since API level 8.
The solution is to use two ImageView objects instead of one.
The first ImageView will be working like before but loaded image will be scaled down so that it's width will be smaller than the width of the ImageView and it's height will be smaller than the height of the ImageView.
The second ImageView will be blank at the start. Everytime the x, y, fw and fh properties are changing the AsyncTask will be executed to load only visible part of the image. When properties are changing fast the AsyncTask will not be able to finish in time. It will have to be canceled and new one will be started. When it finishes the result Bitmap will be loaded onto the second ImageView so it will be visible to user. When the properties changes again loaded Bitmap will be deleted, so it will not cover moving Bitmap loaded to the first ImageView. Note: BitmapRegionDecoder which I will use to load sub-image is available since Android API level 10, so API 8 and API 9 users will only see scaled down image. I decided it is OK.
Code needed:
Setting the first (bottom) ImageView scaleType="matrix" (best in XML)
Setting the second (top) ImageView scaleType="fitXY" (best in XML)
Functions from Android Documentation (here) - thanks to user Vishavjeet Singh.
NOTE: Notice the || operator instead of && while calculating inSampleSize. We want the image loaded to be smaller than ImageView so that we are sure we have enough RAM to load it. (I presume ImageView size is not bigger than the size of the device display. I also presume that the device has enough memory to load at least 2 Bitmaps of the size of the device display. Please tell me if I am making a mistake here.)
NOTE 2: I am loading images using InputStream. To load a file different way you will have to change code in try{...} catch(...){...} blocks.
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;
}
public Bitmap decodeSampledBitmapFromResource(Uri fileUri,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
try {
InputStream is = this.getContentResolver().openInputStream(fileUri);
BitmapFactory.decodeStream(is, null, options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
try {
InputStream is = this.getContentResolver().openInputStream(fileUri);
return BitmapFactory.decodeStream(is, null, options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Function returning a sub-image of an image.
NOTE: Size of Rectangle that will be cut out of source image is relative to the image. Values that specify it are from 0 to 1 because the size of the ImageView and loaded Bitmaps differs from the size of the original image.
public Bitmap getCroppedBitmap (Uri fileUri, int outWidth, int outHeight,
double rl, double rt, double rr, double rb) {
// rl, rt, rr, rb are relative (values from 0 to 1) to the size of the image.
// That is because image moving will be smaller than the original.
if (Build.VERSION.SDK_INT >= 10) {
// Ensure that device supports at least API level 10
// so we can use BitmapRegionDecoder
BitmapRegionDecoder brd;
try {
// Again loading from URI. Change the code so it suits yours.
InputStream is = this.getContentResolver().openInputStream(fileUri);
brd = BitmapRegionDecoder.newInstance(is, true);
BitmapFactory.Options options = new BitmapFactory.Options();
options.outWidth = (int)((rr - rl) * brd.getWidth());
options.outHeight = (int)((rb - rt) * brd.getHeight());
options.inSampleSize = calculateInSampleSize(options,
outWidth, outHeight);
return brd.decodeRegion(new Rect(
(int) (rl * brd.getWidth()),
(int) (rt * brd.getHeight()),
(int) (rr * brd.getWidth()),
(int) (rb * brd.getHeight())
), options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
else
return null;
}
AsyncTask loading the sub-image Bitmap.
NOTE: notice declaring a variable of the type of this class. It will be used later.
private LoadHiResImageTask loadHiResImageTask = new LoadHiResImageTask();
private class LoadHiResImageTask extends AsyncTask<Double, Void, Bitmap> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Bitmap doInBackground(Double... numbers) {
return getCroppedBitmap(
// You will have to change first parameter here!
Uri.parse(imagesToCrop[0]),
numbers[0].intValue(), numbers[1].intValue(),
numbers[2], numbers[3], numbers[4], numbers[5]);
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Bitmap result) {
ImageView hiresImage = (ImageView) findViewById(R.id.hiresImage);
hiresImage.setImageBitmap(result);
hiresImage.postInvalidate();
}
}
Function that will make it all work together.
This function will be called every time the x, y, fw or fh property changes.
NOTE: hiresImage in my code is the id of the second (top) ImageView
private void updateImageView () {
// ... your code to update ImageView matrix ...
//
// imageToCrop.setImageMatrix(m);
// imageToCrop.postInvalidateDelayed(10);
if (Build.VERSION.SDK_INT >= 10) {
ImageView hiresImage = (ImageView) findViewById(R.id.hiresImage);
hiresImage.setImageDrawable(null);
hiresImage.invalidate();
if (loadHiResImageTask.getStatus() != AsyncTask.Status.FINISHED) {
loadHiResImageTask.cancel(true);
}
loadHiResImageTask = null;
loadHiResImageTask = new LoadHiResImageTask();
loadHiResImageTask.execute(
(double) hiresImage.getWidth(),
(double) hiresImage.getHeight(),
// x, y, fw, fh are properties from the question
(double) x / d.getIntrinsicWidth(),
(double) y / d.getIntrinsicHeight(),
(double) x / d.getIntrinsicWidth()
+ fw / d.getIntrinsicWidth(),
(double) y / d.getIntrinsicHeight()
+ fh / d.getIntrinsicHeight());
}
}
Try loading source bitmap by BitmapFactory Options in just decode bounds
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;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
After writing these 2 methods
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
set bitmap on imageview like this
then try scaling the image it will do for a large bitmap efficiently
You can read here further
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
As you already said you could crop your big image and place it one by one to the new imageView.
But from the other side I could suggest you to cut your big image into several small ones and in this case you probably save memory and speed because you won't load the whole image into your memory.
You will gain similar that for example google maps have - they have many small tiles that are loaded by demand. They don't load the whole world map but the small parts of it.
In this case you will build something like ListView when each new item will be an imageview and contains some small image that is part of a big image. By the way with such approach you could get even repeated tiled background and change them on runtime.
I have a problem. I have a large graphic for my main menu/start screen. This causes an out of memory exception on some older devices. It is a PNG file with the resoluton 1920*1080 and I use the ImageFormat RGB565. Do you have any ideas how I can reduce the used ram?
As mentioned you may scale down the image but if for some reason you don't want to or you can't do that you may want to check that link:
http://developer.android.com/training/displaying-bitmaps/index.html
Also you may add
largeHeap=true
To increase memory a little bit but it will work only on new devices, Android 2.x does not support that
This is what you are looking for:
BitmapFactory.Options.inSampleSize
If this is set to true, then the resulting bitmap will allocate its
pixels such that they can be purged if the system needs to reclaim
memory. In that instance, when the pixels need to be accessed again
(e.g. the bitmap is drawn, getPixels() is called), they will be
automatically re-decoded. For the re-decode to happen, the bitmap must
have access to the encoded data, either by sharing a reference to the
input or by making a copy of it. This distinction is controlled by
inInputShareable. If this is true, then the bitmap may keep a shallow
reference to the input. If this is false, then the bitmap will
explicitly make a copy of the input data, and keep that. Even if
sharing is allowed, the implementation may still decide to make a deep
copy of the input data.
Source:
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize
So basically you can adjust the inSampleSize based on the screen resolution and available RAM so that you always have an adequate version of the bitmap for a given device. This will prevent OutOfMemoryError errors from occuring.
Here's an example of how to make use of it:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
Source: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
There's also some more info under that link so I'd recommend that you check it out.
I have searched for this, and i found that for api less than 12, usually what is used is bitmap.getrowbytes * bitmap.getheight;
However, for an image i am obtaining the following:
mBitmap.getRowBytes() = 320
mBitmap.getHeight() = 100
mBitmap.getWidth() = 80.
Thus according to the above formula I get 32,000.
However when i check the file that i m reading the bitmap image from from adb using
ls -l
I get that the size is 90Kb.
I am reading the image as follows:
Uri chosenImageUri = data.getData();
if (chosenImageUri != null) {
try {
InputStream photoStream = getContentResolver().openInputStream(chosenImageUri);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 4;
Bitmap mBitmap = BitmapFactory.decodeStream(photoStream,null, opts);
photoStream.close();
}
}
Why are the results different? Thank you.
Ok I just understood what's going on. I will post this in case it helps anyone.
The reason of this is that I am using bitmapfactoryoptons along with insamplesize =4.
What happens is
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.
as mentioned here
The amount of memory an image takes on the RAM is different from the one on disk. In the RAM, the image's memory consumption can be checked by the formula pixels*3 if it's RGB and pixels*4 if it's ARGB. Number of pixels can be found by width*height. Your image must have an alpha channel, as 80*100*4 = 32,000. Note that this is in Bytes, and to get it in kB or MB you'll need to divide that by 1024 and 1024*1024 respectively.
I am working on an Android game that is pretty heavy in terms of image resources. One of the challenges that I am facing is that I need to manually scale the graphics to match the different screen sizes. The reason for not allowing Android to auto-scale is that the game is dependent upon the graphics not getting stretched or skewed. I will be potentially switching in 3-4 bitmaps per screen press, so performance is key (I already have a caching operation set up to help w/ this).
After considering the problem and the diversity of Android devices out there, I decided on the following approach to target as many devices as possible:
Make density-specific graphics (via http://developer.android.com/guide/topics/resources/providing-resources.html and the other suggestions listed in dev.android.com )
Rather than making many versions of an image for the different screen sizes, create one very large image that covers all target screens and use manual scaling to reduce its size to match the device.
So far, this approach is going well, but I need to squeeze more performance out of my custom BitmapScaler class. I have leveraged the code base found here to suit my own needs: http://zerocredibility.wordpress.com/2011/01/27/android-bitmap-scaling/ .
With that being said, here are my questions: Does anyone have any comments on the viability of my approach for solving the screen destiny / screen size problem? I am aware that I am trading performance for convenience here. Can anyone suggest a way to squeeze out better performance in my bitmap scaling operation (Below)?
public class BitmapScaler {
private Bitmap scaledBitmap;
public Bitmap getScaledBitmap()
{
return scaledBitmap;
}
/* IMPORANT NOTES:
* The process of scaling bitmaps for Android is not a straightforward process. In order to
* help preserve memory on the phone, you need to go through several steps:
*
* 1. Decode the target resource, but use the inJustDecodeBounds option. This allows you to "sample"
* the resource without actually incurring the hit of loading the entire resource. If set to true, the decoder will return null
* (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate
* the memory for its pixels.
* 2. Determine the new aspects of your bitmap, particularly the scale and sample. If the sample size is set to a value > 1,
* requests the decoder to subsample the original image, returning a smaller image to save memory. The sample size is the number
* of pixels in either dimension that correspond to a single pixel in the decoded bitmap. 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.
* Note: the decoder will try to fulfill this request, but the resulting bitmap may have different dimensions that precisely what
* has been requested. Also, powers of 2 are often faster/easier for the decoder to honor.
* 3. Prescale the bitmap as much as possible, rather than trying to fully decode it in memory. This is a less
* expensive operation and allows you to "right size" your image.
* 4. Create your new bitmap, applying a matrix to "fine tune" the final resize.
*
* Partial Ref: http://zerocredibility.wordpress.com/2011/01/27/android-bitmap-scaling/
*/
public BitmapScaler(Resources resources, int targetResourceID, int targetWidth, int targetHeight)
{
BitmapInfo originalInfo = getOriginalBitmapInfo(resources, targetResourceID);
BitmapInfo newInfo = getScaledBitmapInfo(targetHeight, targetWidth, originalInfo);
prescaleScaledBitmap(resources, targetResourceID, newInfo);
scaleScaledBitmap(newInfo);
}
private void scaleScaledBitmap(BitmapInfo newInfo)
{
int ScaledHeight = scaledBitmap.getHeight();
int ScaledWidth = scaledBitmap.getWidth();
float MatrixWidth = ((float)newInfo.width) / ScaledWidth;
float MatrixHeight = ((float)newInfo.height) / ScaledHeight;
Matrix matrix = new Matrix();
matrix.postScale(MatrixWidth, MatrixHeight);
scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, ScaledWidth, ScaledHeight, matrix, true);
}
private void prescaleScaledBitmap(Resources resources, int targetResourceID, BitmapInfo newInfo)
{
BitmapFactory.Options scaledOpts = new BitmapFactory.Options();
scaledOpts.inSampleSize = newInfo.sample;
scaledBitmap = BitmapFactory.decodeResource(resources, targetResourceID, scaledOpts);
}
private BitmapInfo getOriginalBitmapInfo(Resources resources, int targetResourceID)
{
BitmapFactory.Options bitOptions = new BitmapFactory.Options();
bitOptions.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, targetResourceID, bitOptions);
return new BitmapInfo(bitOptions.outHeight,bitOptions.outWidth);
}
private BitmapInfo getScaledBitmapInfo(int targetHeight, int targetWidth, BitmapInfo originalBitmapInfo)
{
float HeightRatio = targetHeight / (float)originalBitmapInfo.height;
float WidthRatio = targetWidth / (float)originalBitmapInfo.width;
BitmapInfo newInfo = new BitmapInfo(0,0);
if (HeightRatio > WidthRatio)
{
newInfo.scale = WidthRatio;
newInfo.width = targetWidth;
newInfo.height = (int)(newInfo.scale * originalBitmapInfo.height);
} else {
newInfo.scale = HeightRatio;
newInfo.height = targetHeight;
newInfo.width = (int)(newInfo.scale * originalBitmapInfo.width);
}
newInfo.sample = 1;
int SampleHeight = originalBitmapInfo.height;
int SampleWidth = originalBitmapInfo.width;
while (true) {
if (SampleWidth / 2 < newInfo.width || SampleHeight / 2 < newInfo.height) {
break;
}
SampleWidth /= 2;
SampleHeight /= 2;
newInfo.sample *= 2;
}
return newInfo;
}
}
Thanks!