So my issue is exactly as the title states. I am downloading many different images and scaling them then setting them to a view. My issue is it seems that only .gif images are visible in the views that are created and the views that should contain .jpg images are almost blank. I say almost because there seems to be a strange tiny black dot on each .jpg view of different shapes, and by tiny I mean the size of a period, so those might be the images but reduced in size too much. Any help... p.s Though outputs I am sure the images are going though my bitmap and makeing it to the setImageBitmap().
My bitmap creating method:
#Override
protected Bitmap doInBackground(String... url) {
String url1 = url[0];
InputStream s = null;
try {
s = (new URL(url1)).openStream();
} catch (MalformedURLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
final BufferedInputStream is = new BufferedInputStream(s, 32*1024);
try {
final Options decodeBitmapOptions = new Options();
// For further memory savings, you may want to consider using this option
// decodeBitmapOptions.inPreferredConfig = Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel
if( parent.getResources().getDisplayMetrics().widthPixels >0) {
final Options decodeBoundsOptions = new Options();
decodeBoundsOptions.inJustDecodeBounds = true;
is.mark(32*1024); // 32k is probably overkill, but 8k is insufficient for some jpgs
BitmapFactory.decodeStream(is,null,decodeBoundsOptions);
is.reset();
final int originalWidth = decodeBoundsOptions.outWidth;
final int originalHeight = decodeBoundsOptions.outHeight;
Debug.out("Inbound image preview for : "+url1);
Debug.out(originalWidth);
Debug.out(originalHeight);
// inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings
decodeBitmapOptions.inSampleSize= Math.max(1,Math.min(originalWidth / 2, originalHeight / 2));
}
return BitmapFactory.decodeStream(is,null,decodeBitmapOptions);
} catch( IOException e ) {
throw new RuntimeException(e); // this shouldn't happen
} finally {
try {
is.close();
} catch( IOException ignored ) {}
}
}
Also here is where I set my new image after download :
protected void onPostExecute(Bitmap map) {
//image is a object of type ImageView that has already been added to to the grand scheme of things
image.setImageBitmap(map);
}
Do I need to update the view or something? If so why do my gifs load fine?
Issue turned out to be with my scaling, particularly this line decodeBitmapOptions.inSampleSize= Math.max(1,Math.min(originalWidth / 2, originalHeight / 2));
Related
I have been developing an app which takes a PDF and then it strips down all its images and stores them as ArrayList of bitmaps. These bitmaps can then further be edited and then saved as PDF. When I am trying to save them as PDF after editing or just without editing the PDF size becomes ten times the original PDF size and though I have made each page processing on a different thread it stills runs very slow.
for example: If I take a PDF of size 28 MB it takes somewhere in the neighborhood of 4 minutes to make them back into PDF. There are 20 images in the PDF and the output PDF size is above 200 MB.
I am using the PDFBox library from Tom Roush for android. Tom Roush PDFBox Repo.
This is the createPdf() method :
public void createPdf() {
document = new PDDocument();
for(Bitmap image : images)
{
PDFPages page=new PDFPages();
page.execute(image);
}
}
The asynctask PDFPages class is as follows :
public class PDFPages extends AsyncTask<Bitmap,Integer,Void>
{
#Override
protected Void doInBackground(Bitmap... voids) {
try {
Bitmap image=voids[0];
PDPage page = new PDPage();
document.addPage(page);
// Define a content stream for adding to the PDF
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PDImageXObject ximage = LosslessFactory.createFromImage(document, image);
// Defining and calculating position and scaling variables
float w = image.getWidth();
float h = image.getHeight();
float x_pos = page.getCropBox().getWidth();
float y_pos = page.getCropBox().getHeight();
if (w > h) {
h = h * (x_pos / w);
w = x_pos;
} else {
w = w * (y_pos / h);
h = y_pos;
}
float x_adjusted = (x_pos - w) / 2;
float y_adjusted = (y_pos - h) / 2;
contentStream.drawImage(ximage, x_adjusted, y_adjusted, w, h);
// Make sure that the content stream is closed:
contentStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
countPages = countPages + 1;
if(countPages == images.size()) {
try {
// Save the final pdf document to a file
final String path = myDir.getAbsolutePath() + "/Created.pdf";
document.save(path);
document.close();
Toast.makeText(process.this, "PDF successfully written to :" + path, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
progressBar.setVisibility(View.INVISIBLE);
saving.setVisibility(View.INVISIBLE);
anim.cancel();
}
}
}
The method used for extracting images out of the PDF is as follows :
public void createImages()
{
try {
//Loading the pdf file
PDDocument document = PDDocument.load(file);
//Getting all the pages in list
PDPageTree pages= document.getDocumentCatalog().getPages();
Iterator iter = pages.iterator();
myDir = new File(root.getAbsolutePath(), "PDF/" + pdfName);
if (!myDir.exists()) {
myDir.mkdirs();
}
// i used for counting number of images
i=0;
while(iter.hasNext())
{
PDPage page=(PDPage) iter.next();
PDResources resources=page.getResources();
//Tom Roush code that he commented against my issue of not having resources.getImages() method
for (COSName name : resources.getXObjectNames())
{
PDXObject xobj = resources.getXObject(name);
if (xobj instanceof PDImageXObject)
{
bit = ((PDImageXObject)xobj).getImage();
//Image acquired.
if(bit != null) {
images.add(bit);
}
i=i+1;
}
}
}
if(i == 0)
{
Intent intent=new Intent(process.this,MainActivity.class);
intent.putExtra("images",i);
startActivity(intent);
}
document.close();
}
catch (Exception e)
{
e.printStackTrace();
}
Log.i("helll","Completed CreateImages()");
}
images is the ArrayList of bitmaps.
The input PDF is the PDF made by Cam Scanner (the app) with the use of 20 images taken by the device's camera. It has a size of 27.45 MB and the output PDF has a size of
264.10 MB
I will upload the PDFs shortly.
The reason why I can't upload: I am currently away from my workspace and I am entirely dependent on my phone's internet and yes I live in a third world country. So I will upload the PDFs in my google drive and edit in the links as soon as I get some decent internet connection.
I want some method by which I can lower the time of outputting and the size of the outputted PDF.
I am using itext library in which i have to put image which scale properly in landscape mode . However if i changes the page to landscape mode ,the images which holds there also gets rotated . i don't want that.And if i rotate the image it will behave differently ,it will not rotate from centre.
However ,what i want the image should not rotate ,and it should appear as follows
here is my code
Document document=new Document();
try {
File file=new File(Environment.getExternalStorageDirectory(),"mypdfimage.pdf");
PdfWriter.getInstance(document, new FileOutputStream(file));
document.open();
Image image = null;
try {
image = Image.getInstance (Environment.getExternalStorageDirectory()+"/image.jpg");
int identation=0;
//Rectangle rectangle=document.getPageSize();
Rotation rotation=new Rotation();
pdfwriter.setPageEvent(rotation);
//image.scalePercent(scalerX, scalerY);
//PdfDictionary pageDict=null;
// pageDict.put(PdfName.ROTATE, new PdfNumber(90));
//pdfwriter.addPageDictEntry(PdfName.ROTATE, PdfPage.LANDSCAPE);
image.scaleToFit(PageSize.A4.getWidth() - document.leftMargin() - document.rightMargin(), PageSize.A4.getHeight() - document.topMargin() - document.bottomMargin());
// image.setRotationDegrees(90);
// image.setAlignment(Element.ALIGN_CENTER);
document.add(image);
document.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public class Rotation extends PdfPageEventHelper
{
#Override
public void onStartPage(PdfWriter writer, Document document) {
writer.addPageDictEntry(PdfName.ROTATE,PdfPage.LANDSCAPE);
}
}
You are rotating the page the wrong way. You probably took an example that was written in answer to a question asking how to rotate the page and its content. That is the exception. If you follow the normal examples on how to rotate a page, the page will be rotated, but not the image.
Please take a look at the ImageOnRotatedPage example:
public void createPdf(String dest) throws IOException, DocumentException {
Document document = new Document(PageSize.A4.rotate());
PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
Image img = Image.getInstance(IMAGE);
img.scaleToFit(770, 523);
float offsetX = (770 - img.getScaledWidth()) / 2;
float offsetY = (523 - img.getScaledHeight()) / 2;
img.setAbsolutePosition(36 + offsetX, 36 + offsetY);
document.add(img);
document.close();
}
As you can see, I create a rotated A4 page by using the rotate() method:
Document document = new Document(PageSize.A4.rotate());
I also scale the image so that it fits the page and I calculate an offset so that it is nicely centered on the page. See cardiogram.pdf:
This looks exactly the way you want it to look and you don't need to resort to using page events and changing the page dictionary.
My internet connection is very fast. Despite that my image loads very slow in the app. Here is the code i have been using. As suggested I have been using async method to perform certain task seperately..
public class ItemPage extends Activity{
ImageView image;
String url;
#Override
protected void onCreate(Bundle savedInstanceState) {
image = (ImageView) findViewById(R.id.img);
//getting url from the parent activity
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if(extras != null) url = extras.getString("bigurl");
//async call
new DownloadFilesTask().execute();
}
private class DownloadFilesTask extends AsyncTask<Void, Void, Void> { //working part
Drawable drawable;
#Override
protected Void doInBackground(Void... params) {
try {
drawable =drawableFromUrl(url);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void result) {
image.setImageDrawable(drawable);
}
}
public static Drawable drawableFromUrl(String url) throws IOException {
Bitmap x;
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.connect();
InputStream input = connection.getInputStream();
x = BitmapFactory.decodeStream(input);
return new BitmapDrawable(x);
}
}
Please help me. Is there any way to load the image faster from the url? Thanks in advance for your time.
i think you want to use Aquery Library .
it is load images very speedly.
refer this link
This works for me.
Bitmap mIcon_val;
URL newurl;
private void loadImage() {
Thread backgroundThread = new Thread() {
#Override
public void run() {
// do second step
try {
newurl = new URL(image_url);
mIcon_val = BitmapFactory.decodeStream(newurl
.openConnection().getInputStream());
} catch (MalformedURLException e) {
} catch (IOException e) {
}
Message msg = Message.obtain();
msg.what = 123;
handler.sendMessage(msg);
}
};
}
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 123:
imageView.setImageBitmap(mIcon_val);
break;
}
}
};
For calling the thread, you can use
backgroundThread.start();
My internet connection is very fast. Despite that my image loads very slow in the app.
It doesnt mean you can download image very fast when you have a really fast connection, it could be that the web server have a limit download speed for each connection, or the server is far from your current location. I would recommend to check the download speed of the server you are trying to connection first, if it is slow then it is not code but the server itself.
you may try using picasso library.it has several functions to compress,crop images that may help in downloading images at better rates.the link provides the full documentation of usage of the library-
http://square.github.io/picasso/
Here's something you can do to speed up the image processing side of things.
When you decode the stream you are converting it to an in-memory raster format. A 32bit image at 500x500 will take 1 mb of memory (500x500x4bytes). the native pixel format varies by device, and the fastest performance is to match the bitmap's pixel format to the system's pixel format so the system doesn't need to convert the image from it's native format to the window format.
Older devices in particular will use the 16 bit format. The image would be stored at 32bits, and then converted to 16bits when it's drawn to the screen. On these devices, you would be saving half the memory storage and avoiding a costly pixelwise conversion by decoding the image at 16 bits instead of at 32 bits. On newer devices that have the memory, it's better to store the image at 32 bits to avoid the conversion penalty.
So somewhere early on, store the pixel format that is used by the system. It will either be a 16 bit format (RGB_565) or a 32 bit format (8bits per pixel per color)
// default to 32 bits per pixel
Config bitmapConfig = Bitmap.Config.ARGB_8888 ; //RGB_565 | ARGB_8888
WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
int bmp_cfg = wm.getDefaultDisplay().getPixelFormat();
if (bmp_cfg == PixelFormat.RGBA_8888 || bmp_cfg == PixelFormat.RGBX_8888 || bmp_cfg == PixelFormat.RGB_888){
bitmapConfig = Bitmap.Config.ARGB_8888;
} else {
bitmapConfig = Bitmap.Config.RGB_565;
}
Later, when you are decoding the bitmap, use the window manager's default pixel format so that it will create the bitmap with the correct pixel format while decoding (rather than converting in a second pass)
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = bitmapConfig; // match pixel format to widnow system's format
Bitmap bitmap = BitmapFactory.decodeStream(input, null, options);
bitmap = Bitmap.createBitmap(bitmap);
You can also play with the sample size to adjust the bitmap, but at 500x500 you probably need the whole image anyway.
I am 100% sure the problem here is with the actual image. However I hope that the solution is some attribute of the image that will help others in the future.
The image:
the photo in question http://soundwave.robotsidekick.com/mlsphotos.jpg
I have tried loading this image in several ways. I have downloaded it and tried loading it in an ImageView:
final ImageView v = (ImageView) findViewById(R.id.image);
v.setImageResource(R.drawable.photo);
v.invalidate();
I have tried loading it from a url:
final String[] params = new String[] {
"",
};
(new AsyncTask<String, Bitmap, Bitmap>()
{
#Override
protected Bitmap doInBackground(final String... params)
{
Bitmap ret = null;
for (final String url : params)
{
try
{
Log.e(TAG, url);
ret = BitmapFactory.decodeStream((new URL(url)).openStream());
publishProgress(ret);
}
catch (final MalformedURLException e)
{
Log.e(TAG, "Malformed URL", e);
}
catch (final IOException e)
{
Log.e(TAG, "IO Exception", e);
}
}
return ret;
}
#Override
protected void onProgressUpdate(final Bitmap... values)
{
super.onProgressUpdate(values);
for (final Bitmap result : values)
{
if (result != null)
{
final ImageView v = (ImageView) MainActivity.this.findViewById(R.id.image);
v.setImageBitmap(result);
v.invalidate();
}
}
}
}).execute(params);
I have also tried loading the image in a WebView like this:
final WebView webview = (WebView) findViewById(R.id.webview);
webview.loadData("<html><body><img src=\"" + url + "\"></body></html>", "text/html", "utf-8");
webview.invalidate();
I have also tried loading the image in Browser (the app) and that does not work.
None of those work, HOWEVER if I load the url into Chrome on Android it works great (not in Browser), if I load the image on my desktop browser (Chrome, Firefox, etc) it loads great. I have checked that the mime type matches the extension and I am just at a loss.
EDIT
There is a work around for images coming from an InputStream where the bitmap processing runs out of data on the stream before the stream completes. The work around is documented in this question and this bug.
However this is a corrupt image whose data ends prematurely. I know that means I am already down a broken track, but I am hoping to have some better error handling than Android passing me back null and I lose. iOS, Chrome (on device and computer) as well as most other places seem to have much better error handling. Is there something I can do on Android to handle corrupt jpgs?
EDIT 2
There has to be a solution here because Chrome on my device handles this situation elegantly. However the closest I can come to fixing this is the following code:
final InputStream is = (new URL(url)).openStream();
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final int size = 1024;
int len = -1;
byte[] buf = new byte[size];
while ((len = is.read(buf, 0, size)) != -1)
{
bos.write(buf, 0, len);
}
buf = bos.toByteArray();
// buf is now filled with the corrupted bytes of the image
ret = BitmapFactory.decodeByteArray(buf, 0, buf.length);
// ret is null because it was a corrupt jpg
With that code I can check if there are bytes and the image wasn't decoded. Then at least I can tell I have a corrupt image (or not an image) and can report something slightly more useful to the user (like hey I have an image here with 16K but I sure don't know what to do with it).
Anyone know how Chrome manages to decode as much of the image as they can before they hit the corruption?
I opened the file in Photoshop CS6 and it said that the file may be damaged, possibly truncated or incomplete. The file can be opened. If I save it in Photoshop without making any changes, it then works in Android. I'm afraid I don't know exactly what's wrong with the image though.
Here is the important bit about JPGs from Wikipedia and here's a question that ultimate led me to the solution.
I just appended the two closing jpeg end of image bytes to the stream, in order to convince the decoder that the stream is done with image data. This method is flawed because JPGs can have JPGs inside them, meaning appending one set of end of image bytes, doesn't guarantee that we closed all the images.
In both solutions below I assume is is an input stream for a JPG image. Also these two constants are defined:
private static final int JPEG_EOI_1 = 0xFF;
private static final int JPEG_EOI_2 = 0xD9;
This method we read all the bytes into memory then try to decode the bytes:
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final int size = 1024;
int len = -1;
final byte[] buf = new byte[size];
try
{
while ((len = is.read(buf, 0, size)) != -1)
{
bos.write(buf, 0, len);
}
bos.write(JPEG_EOI_1);
bos.write(JPEG_EOI_2);
final byte[] bytes = bos.toByteArray();
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}
catch (final IOException ex)
{
return null;
}
catch (final Exception ex)
{
return null;
}
This method creates a stream wrapper that makes sure the last two bytes are JPG end of image bytes:
return BitmapFactory.decodeStream(new JpegClosedInputStream(is));
// And here's the stream wrapper
class JpegClosedInputStream extends InputStream
{
private final InputStream inputStream;
private int bytesPastEnd;
private JpegClosedInputStream(final InputStream iInputStream)
{
inputStream = iInputStream;
bytesPastEnd = 0;
}
#Override
public int read() throws IOException
{
int buffer = inputStream.read();
if (buffer == -1)
{
if (bytesPastEnd > 0)
{
buffer = JPEG_EOI_2;
}
else
{
++bytesPastEnd;
buffer = JPEG_EOI_1;
}
}
return buffer;
}
}
do setcontentview and show it from the xml like that:
//photo.xml
<ImageView
android:id="#+id/picture"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_gravity="center"
android:src="#drawable/photo" />
/>
//Photo.java
setContentView(R.layout.photo);
I'm loading approximately 20 pictures of size 50-70K from server, then display them in a ListView. Initially I stored the data as Bitmap which causes memory running out quickly. Then I decided to compress all these bitmap files and store in Content provider Media. So in my adapter, the user data only contains the Uri to the image file.
However it didn't fix the problem at all, it run a bit longer, but still crashed after loading about 10 pictures or so. Here is the error log from the compiler.
1048576-byte external allocation too large for this process
VM won't let us allocate 1048576 bytes
I even clean up each bitmap data after setting it to my ImageView, plus delete the all the image files which are stored in my sdcard
#Override
public void onDestroy() {
// clean up
for (User user : userList) {
getContentResolver().delete(user.getImageUri(), null, null);
}
super.onDestroy();
}
private Uri constructUriFromBitmap(Bitmap bitmap) {
ContentValues values = new ContentValues(1);
values.put(Media.MIME_TYPE, "image/jpeg");
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
try {
OutputStream outStream = getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
outStream.close();
}
catch (Exception e) {
Log.e(TAG, "exception while writing image", e);
}
bitmap.recycle();
return uri;
}
Now I ran out of idea, I really don't know what could go wrong in this case. I wonder if anyone has experienced this issue could shed me some lights?
Since the code is quite long, I only extract the main functions:
Here is my User class data:
public class FriendFeed {
// required parameters
private final int activityId; // in case we want to handle the detail of
// this activity
private final int friendId;
private final String friendName;
private final Challenge.Type challengeType;
private final String activityTime;
private final String placeName;
// optional parameter
private String challengeName;
private String challengeDescription;
private Uri activitySnapPictureUri = null;
private Uri friendPictureUri = null;
private String activityComment;
And here is my main function:
#Override
protected Boolean doInBackground(Void...voids) {
JSONArray array = JsonHelper.getJsonArrayFromUrlWithData(GET_FRIEND_FEED_URL, datas);
if (array != null) {
try {
for (int i = 0; i < array.length(); ++i) {
Uri snapPictureUri = null;
Uri userPictureUri = null;
if (Challenge.returnType(array.getJSONObject(i).getString("challenges_tbl_type")) == Challenge.Type.SNAP_PICTURE) {
snapPictureUri = constructUriFromBitmap(ImageHelper.downloadImage(array.getJSONObject(i).getString("activity_tbl_snap_picture_url")));
}
if(ImageHelper.downloadImage(array.getJSONObject(i).getString("users_tbl_user_image_url")) != null) {
userPictureUri = constructUriFromBitmap(ImageHelper.downloadImage(array.getJSONObject(i).getString("users_tbl_user_image_url")));
}
publishProgress(
new FriendFeed.Builder(
// required parameters
array.getJSONObject(i).getInt("activity_tbl_id"),
array.getJSONObject(i).getInt("friends_tbl_friend_id"),
array.getJSONObject(i).getString("users_tbl_username"),
Challenge.returnType(array.getJSONObject(i).getString("challenges_tbl_type")),
array.getJSONObject(i).getString("activity_tbl_created"),
array.getJSONObject(i).getString("spots_tbl_name"))
// optional parameters
.challengeName(array.getJSONObject(i).getString("challenges_tbl_name"))
.challengeDescription(array.getJSONObject(i).getString("challenges_tbl_description"))
.activitySnapPictureUri(snapPictureUri)
.friendPictureUri(userPictureUri)
.activityComment(array.getJSONObject(i).getString("activity_tbl_comment"))
.build());
}
}
catch (JSONException e) {
Log.e(TAG + "GetFriendFeedTask.doInBackGround(Void ...voids) : ", "JSON error parsing data" + e.toString());
}
return true;
}
else {
return false;
}
}
Android enforces a per-process memory allocation limit of 24MB so you can't allocate more than that. However, 20 pics of 70K each should amount to 1.4MB only... so my guesses:
Maybe you're allocating Bitmaps in other parts of your app, so that there's less than 1.4MB available for your bitmaps on this ListView.
Memory leak somewhere
If you determine that you really need all the bitmaps you're using, are you sure you need the bitmaps to be this large or have this much resolution? Reducing them can help.
If all else fails and you do need lots of bitmaps in memory, you can always use OpenGL textures.