I'm showing lot of pdfs using recyclerview. I'm converting first page of a pdf into image and then showing it beside the pdf file. I'm using glide library for loading those bitmaps. But, recyclerview lags while scrolling. How to make the scrolling smooth?
Code for converting pdf to bitmap:-
public static Bitmap pdfToBitmap(File pdfFile) {
Bitmap bitmap = null;
try {
PdfRenderer renderer = new PdfRenderer(ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY));
final int pageCount = renderer.getPageCount();
if (pageCount > 0) {
PdfRenderer.Page page = renderer.openPage(0);
int width = (int) (page.getWidth());
int height = (int) (page.getHeight());
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
page.close();
renderer.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
return bitmap;
}
Code for loading the bitmap into imageview:
Glide.with(viewHolder.itemView.getContext())
.load(bitmap)
.into(viewHolder.pdfImg);
Related
I have PDF files that contains 1 single icon.
I need to render this icons in GridView.
On Android 5 I'm using PdfRenderer system class and it works amazing:
On Android 4, I'm using "Android-Pdf-Viewer-Library".
https://github.com/jblough/Android-Pdf-Viewer-Library
It works, but the quality is poor:
Can you please suggest any alternatives for rendering PDF as Bitmap?
Or maybe there is a way to increase quality of rendering for that library?
Here is my code:
private static Bitmap renderToBitmap(Context context, InputStream inStream) {
Bitmap bitmap = null;
try {
byte[] decode = IOUtils.toByteArray(inStream);
ByteBuffer buf = ByteBuffer.wrap(decode);
PDFPage mPdfPage = new PDFFile(buf).getPage(0, true);
float width = mPdfPage.getWidth();
float height = mPdfPage.getHeight();
bitmap = mPdfPage.getImage((int) (width), (int) (height), null, true,
true);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inStream.close();
} catch (IOException e) {
}
}
return bitmap;
}
Currently I'm loading MediaStore Image Thumbnails using picasso into the ListView with the following snippet: (video.getData() returns the actual path of the image such as mnt/sdcard/...)
Picasso.with(this.context)
.load(new File(photo.getData()))
.resize(50, 50).config(config)
.centerCrop()
.into(viewHolder.imageViewItem);
Now I'm and unable to load the MediaStore Video Thumbnails by just passing the video.getData() instead of photo.getData()?
First You need to create VideoRequestHandler
public class VideoRequestHandler extends RequestHandler{
public String SCHEME_VIDEO="video";
#Override
public boolean canHandleRequest(Request data)
{
String scheme = data.uri.getScheme();
return (SCHEME_VIDEO.equals(scheme));
}
#Override
public Result load(Request data, int arg1) throws IOException
{
Bitmap bm;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
try {
Size size = new Size(250, 250);
bm = ThumbnailUtils.createVideoThumbnail(new File(data.uri.getPath()), size, null);
} catch (IOException e) {
e.printStackTrace();
}
}
else
{
bm = ThumbnailUtils.createVideoThumbnail(data.uri.getPath(), MediaStore.Images.Thumbnails.MINI_KIND);
}
return new Result(bm,LoadedFrom.DISK);
}
}
After That
VideoRequestHandler videoRequestHandler;
Picasso picassoInstance;
Build only once
videoRequestHandler = new VideoRequestHandler();
picassoInstance = new Picasso.Builder(context.getApplicationContext())
.addRequestHandler(videoRequestHandler)
.build();
Then load file from path
picassoInstance.load(VideoRequestHandler.SCHEME_VIDEO+":"+filepath).into(holder.videoThumbnailView);
Updated October 2020
ThumbnailUtils.createVideoThumbnail(data.uri.getPath(), MediaStore.Images.Thumbnails.MINI_KIND);
is deprecated in Android Q. I will write in Kotlin:
val bm = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ThumbnailUtils.createVideoThumbnail(
File(data.uri.path!!),
Size(200f.toPx(), 200f.toPx()),
CancellationSignal()
)
} else {
ThumbnailUtils.createVideoThumbnail(
data.uri.path!!,
MediaStore.Images.Thumbnails.MINI_KIND
)
}
to Px is an extension function which is like below;
fun Float.toPx() =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)
.toInt()
You can use any dp value for it :) I hope this can help you :)
Before, you had two options:
1) Do a call beforehand, when you were retrieving your videos for example and storing the bitmap:
Bitmap thumbnailBitmap = MediaStore.Video.Thumbnails.getThumbnail(content, id, MediaStore.Video.Thumbnails.MINI_KIND, options);
2) Creating a custom Downloader (not verified for local media)
But now, with Picasso 2.4, you can create a RequestHandler. You can have a look at this page for example: http://blog.jpardogo.com/requesthandler-api-for-picasso-library/
In my current usage, I fetch the thumbnail path and then call Picasso with that path. To get the thumbnail path, I referred to: android get video thumbnail PATH, not Bitmap.
public void bindTo(MediaListHolder mediaListRowHolder, int i) {
DataPictures message = itemList.get(i);
try {
Uri uri = Uri.fromFile(new File(message.getFilePath()));
if (message.getFileType().equalsIgnoreCase("video")) {
Bitmap bmThumbnail = ThumbnailUtils.extractThumbnail(ThumbnailUtils.createVideoThumbnail(message.getFilePath(),
MediaStore.Video.Thumbnails.FULL_SCREEN_KIND), MAX_WIDTH, MAX_HEIGHT);
mediaListRowHolder.thumbnail.setImageBitmap(bmThumbnail);
} else {
Picasso.with(ApplicationSingleton.getInstance()).load(uri)
.resize(size, size)
.placeholder(R.drawable.logo_slogan)
.into(mediaListRowHolder.thumbnail);
}
Log.i(TAG, "bindTo: ");
} catch (Exception e) {
e.printStackTrace();
}
}
Note - Follow this comment if you simply want to show thumbnail in your image view and apply no extra Picasso functions on it .
I just modified it a little to show circular thumbnail coz it looks cool .
Don't use Picasso . Here's a very simple way to do it .
videoPathUrl = /*your Video File Url */
Bitmap bMap = ThumbnailUtils.createVideoThumbnail(videoPathUrl , MediaStore.Video.Thumbnails.MICRO_KIND);
bMap = transformBitmapToCircularBitmap(bMap);
yourImageView.setImageBitmap(bMap);
and transformBitmapToCircularBitmap function is as follows -
public static Bitmap transformBitmapToCircularBitmap(Bitmap source)
{
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(squaredBitmap,
BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
paint.setShader(shader);
paint.setAntiAlias(true);
float r = size / 2f;
canvas.drawCircle(r, r, r, paint);
squaredBitmap.recycle();
return bitmap;
}
I'm working with a set of layered images (think stacked) and I need to combine them into one element.
I'm basing my solution off Combine multiple bitmap into one
//send a map to the method that has my stored image locations in order
private Bitmap combineImageIntoOne(NavigableMap<Integer, String> layerImages) {
//size of my bitmaps
int w = 400, h = 400;
//bitmap placeholder
Bitmap productIndex = null;
//flattened layers
Bitmap temp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
//canvas to write layers to
Canvas canvas = new Canvas(temp);
int top = 0;
for (Map.Entry<Integer, String> e : layerImages.entrySet()) {
//create the layer bitmap
productIndex = decodeSampledBitmapFromResource(getResources(), e.getValue(), 400, 400);
//add layer to canvas
canvas.drawBitmap(productIndex, 0f, top, null);
}
//convert temp to a BitmapDrawable
Drawable d = new BitmapDrawable(getResources(),temp);
//set my image view to have the flattened image
carBase.setImageDrawable(d);
return temp;
}
The decodeSampledBitmapFromResource come from the Android docs about loading large bitmaps: Loading Large Bitmaps Efficiently You can review the code on that doc to see what I"m doing. I didn't edit the Android code much.
I've been using the Android code just fine to add layers to the FrameLayout but ended up running out of memory when the layers starting getting pretty high in number. This combining method is being used to conserve memory space.
Any ideas why the final bitmap doesn't have any content?
Reference LINK <-------------------------
public Bitmap combineImages(Bitmap c, Bitmap s) { // can add a 3rd parameter 'String loc' if you want to save the new image - left some code to do that at the bottom
Bitmap cs = null;
int width, height = 0;
if(c.getWidth() > s.getWidth()) {
width = c.getWidth() + s.getWidth();
height = c.getHeight();
} else {
width = s.getWidth() + s.getWidth();
height = c.getHeight();
}
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(c, 0f, 0f, null);
comboImage.drawBitmap(s, c.getWidth(), 0f, null);
// this is an extra bit I added, just incase you want to save the new image somewhere and then return the location
/*String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png";
OutputStream os = null;
try {
os = new FileOutputStream(loc + tmpImg);
cs.compress(CompressFormat.PNG, 100, os);
} catch(IOException e) {
Log.e("combineImages", "problem combining images", e);
}*/
return cs;
}
I am working with pdf files, I want to implement page viewer to my pdf file. My idea is to convert pdf file into bmp images, and then to use viewPager. but I am stuck in converting pdf to bitmap. Any suggestions?
Include dependencies in your gradle
compile 'com.github.barteksc:android-pdf-viewer:2.8.1'
Use following function to convert PDF page to bitmap image
private Bitmap generateImageFromPdf(String assetFileName, int pageNumber, int width, int height) {
PdfiumCore pdfiumCore = new PdfiumCore(mActivity);
try {
File f = FileUtils.fileFromAsset(mActivity, assetFileName);
ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
PdfDocument pdfDocument = pdfiumCore.newDocument(fd);
pdfiumCore.openPage(pdfDocument, pageNumber);
//int width = pdfiumCore.getPageWidthPoint(pdfDocument, pageNumber);
//int height = pdfiumCore.getPageHeightPoint(pdfDocument, pageNumber);
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
pdfiumCore.renderPageBitmap(pdfDocument, bmp, pageNumber, 0, 0, width, height);
//saveImage(bmp, filena);
pdfiumCore.closeDocument(pdfDocument);
return bmp;
} catch(Exception e) {
//todo with exception
}
return null;
}
this is a way to render pdf to image
its take 20 to 25 seconds to render pdf file to image
its working in android 10 , android 9 and all lower versions
private void generateImageFromPdf() {
try {
PDDocument doc=PDDocument.load(new File(fileurl));
PDFRenderer pdfRenderer = new PDFRenderer(doc);
Bitmap bffim = pdfRenderer.renderImageWithDPI(0, 100, Bitmap.Config.RGB_565);
String fileName = "image-" + 0 + ".png";
img.setImageBitmap(bffim);
} catch (IOException e) {
e.printStackTrace();
}
}
I am trying to load some very small images (average size is 90kb) into a gridview in Android. Whenever I load more than 9 images then I am getting memory issues. I have tried scaling the images to a smaller size and although this works to a certain extent it is not really a true solution as the picture quality is awful.
The code is below
private Context mContext;
private ArrayList<Bitmap> photos = new ArrayList<Bitmap>();
public Bitmap [] mThumbIds;
public ImageAdapter(Context c) {
mContext = c;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public Bitmap scaleBitmap(String imagePath) {
Bitmap resizedBitmap = null;
try {
int inWidth = 0;
int inHeight = 0;
InputStream in;
in = new FileInputStream(imagePath);
// decode image size (decode metadata only, not the whole image)
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
in.close();
in = null;
// save width and height
inWidth = options.outWidth;
inHeight = options.outHeight;
// decode full image pre-resized
in = new FileInputStream(imagePath);
options = new BitmapFactory.Options();
// calc rought re-size (this is no exact resize)
options.inSampleSize = Math.max(inWidth/300, inHeight/300);
// decode full image
Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);
// calc exact destination size
Matrix m = new Matrix();
RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
RectF outRect = new RectF(0, 0, 300, 300);
m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
float[] values = new float[9];
m.getValues(values);
// resize bitmap
resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return resizedBitmap;
}
public void populateGrid() {
File sdDir = new File("mnt/sdcard/Pictures");
File[] sdDirFiles = sdDir.listFiles();
for(File singleFile : sdDirFiles) {
String filePath = singleFile.getAbsolutePath();
Bitmap bmp = scaleBitmap(filePath);
photos.add(bmp);
}
mThumbIds = photos.toArray(new Bitmap[(photos.size())]);
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageBitmap(mThumbIds[position]);
return imageView;
}
#Override
public int getCount() {
return mThumbIds.length;
}
}
Two considerations:
The 90kb compressed image size doesn't really matter. The memory use is dictated by the actual resolution of the bitmap -- in this case, 300*300*4bpp, so about 360k per bitmap.
Gingerbread has some flaws because of the combination of the fact that Bitmap memory is stored in a native array (rather than on the Java heap) combined with the fact that garbage collection occurs concurrently. Because of this fact, it sometimes takes the memory manager longer to realize that Bitmap memory can be re-used.
So, the ramifications of this are:
Consider the actual decompressed Bitmap size when estimating memory usage.
Recycle intermediate bitmaps as much as possible, to help get the memory reclaimed faster. For example, if you are scaling a bitmap, save a reference to the pre-scaled source, and recycle the source after scaling is complete (compare to the result of the scaling, since it's possible that the same Bitmap is returned).
If you can, test your code on an ICS device. You can then use the heap inspection tools to get a sense about where the most memory is being used. Sine ICS allocates bitmap memory on the Java heap, you'll have an accurate picture of the bitmap memory usage.
Bitmap use a lot of memory, if you have the image saved into memory already it would probably be better to just use the path of the file and push it on an ImaveView.
ImageView img = new ImageView(getApplicationContext());;
img.setImageBitmap(BitmapFactory.decodeFile(media.getThumbPath()));
img.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
myLinearLayout.addView(img);
Something like this might work better. this way you are not storing all the Bitmaps into your heap.