GridView only create Images when they would be visible - java

Hei there
So I have the following problem. I have around 1500 images of playing cards. I want to display them in a "Gallery" where you could scroll through them. I was able to create a GridView with the ImageCell object and I was also able to add images to it. Now my problem is that if I add all Image's at once logically Java crashes because of a heap error. I have the image url's (local files) in a list. How could I implement that it only load lets say 15 images. If I then scroll it loads the next 15 and unloads the old ones. So it would only load the images of the actual visible images and not all 1500. How would I do this? I am completely out of ideas.
The Platform.runLater() is needed because some sort of issue with ControlsFX
Here my code:
public void initialize() {
GridView<Image> gridView = new GridView<>();
gridView.setCellFactory(gridView1 -> new ImageGridCell(true));
Image image = new Image("C:\\Users\\nijog\\Downloads\\cardpictures\\01DE001.png");
gridView.setCellWidth(340);
gridView.setCellHeight(512);
//Platform.runLater(()-> {
// for (int i = 0; i < 5000; i++){
// gridView.getItems().add(image);
// }
//});
Platform.runLater(() -> gridView.getItems().addAll(createImageListFromCardFiles()));
borderPane.setCenter(gridView);
}
protected List<Image> createImageListFromCardFiles(){
List<Image> imageViewList = new ArrayList<>();
App.getCardService().getCardArray().stream()
//.filter(Card::isCollectible)
.sorted(Comparator.comparingInt(Card::getCost))
.sorted(Comparator.comparing(Card::isChampion).reversed())
.skip(0)
//.limit(100)
.forEach(card -> {
try {
String url = String.format(App.pictureFolderPath +"%s.png", card.getCardCode());
imageViewList.add(new Image(url));
} catch (Exception e) {
System.out.println("Picture file not found [CardCode = " + card.getCardCode() + "]");
App.logger.writeLog(Logger.Operation.EXCEPTION, "Picture file not found [CardCode = " + card.getCardCode() + "]");
}
});
return imageViewList;
}

You might not need to use the strategy you describe. You're displaying the images in cells of size 340x512, which is 174,080 pixels. Image storage is 4 bytes per pixel, so this is 696,320 bytes per image; 1500 of them will consume about 1GB. You just need to make sure you load the image at the size you are displaying it (instead of its native size):
// imageViewList.add(new Image(url));
imageViewList.add(new Image(url, 340, 512, true, true, true));
If you need an image at full size later (e.g. if you want the user to select an image from your grid view and display it in a bigger pane), you'd just need to reload it from the url.
If you do need to implement the strategy you describe, GridView supports that out of the box. Just keep a list of the URLs, instead of the Images, and use a custom GridCell to load the image as needed. This will consume significantly less memory, at the cost of a lot more I/O (loading the images) and CPU (parsing the image format).
Make the items for the GridView the image urls, stored as Strings.
Then you can do something like:
GridView<String> gridView = new GridView<>();
gridView.getItems().addAll(getAllImageURLs());
gridView.setCellFactory(gv -> new GridCell<>() {
private final ImageView imageView = new ImageView();
{
imageView.fitWidthProperty().bind(widthProperty());
imageView.fitHeightProperty().bind(heightProperty());
imageView.setPreserveRatio(true);
}
#Override
protected void updateItem(String url, boolean empty) {
super.updateItem(url, empty);
if (empty || url == null) {
setGraphic(null);
} else {
double w = getGridView().getCellWidth();
double h = getGridView().getCellHeight();
imageView.setImage(new Image(url, w, h, true, true, true));
setGraphic(imageView);
}
}
});
protected List<String> getAllImageURLs(){
return App.getCardService().getCardArray().stream()
// isn't the first sort redundant here?
.sorted(Comparator.comparingInt(Card::getCost))
.sorted(Comparator.comparing(Card::isChampion).reversed())
.map(card -> String.format(App.pictureFolderPath +"%s.png", card.getCardCode()))
.collect(Collectors.toList());
}

Related

How to list out only either static or animated images in Android?

I want to detect and display most recent images and it should be either animated or static images on user choice.
Also cannot depend on extentions.Because
There would be absence of extention
webp images could be animated or static with same extention (.webp)
It could be wrong extentions
Is there way to identify whether images are animated (webp, gif, apng) or static (jpg, png, webp, etc)?
private static final String[] COLUMNS_OF_INTEREST = new String[]
{
MediaStore.Video.Media._ID,
MediaStore.Video.Media.DATA,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media.WIDTH,
MediaStore.Video.Media.HEIGHT,
MediaStore.Video.Media.DATE_ADDED
};
public void printGifUri(Context context)
{
ContentResolver cr = context.getContentResolver();
String selection = MediaStore.Images.Media.MIME_TYPE + "=?";
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("webp");
String[] selectionArgsPdf = new String[]{ mimeType };
Cursor gifCursor = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, COLUMNS_OF_INTEREST, selection,selectionArgsPdf,
MediaStore.Images.Media.DATE_ADDED + " DESC");
gifCursor.moveToFirst();
int columnIndexUri = gifCursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
for (int i = 0; i < gifCursor.getCount(); i++)
Log.d("gif file uri -> ", gifCursor.getString(columnIndexUri));
}
I want to also avoid the third party modules as they would increase the size of the app. You can suggest if there is any module available for this with low size.
I have Tried checking the existance of flags like ANIM, VP8, in image file and it works well but this explicit method is time consuming.
The major issue in this is webp as same extention would be static or animated. This makes difficult me to identify the animated and static type.
I think I maybe found a solution... try using
ImageDecoder
the documentation says
If the encoded image is an animated GIF or WEBP, decodeDrawable will
return an AnimatedImageDrawable.
So maybe checking that the result of ImageDecoder.decodeDrawable(source); is an instance of AnimatedImageDrawable should help you figure out if a file is animated or not
such as:
ImageDecoder.Source source = ImageDecoder.createSource(cr,uri);
Drawable drawable = ImageDecoder.decodeDrawable(source);
if (drawable instanceof AnimatedImageDrawable) {
//THE FILE IS ANIMATED
}else{
//THE FILE IS STATIC
}
or such as
private boolean isAnimated(ContentResolver cr,Uri uri){
ImageDecoder.Source source = ImageDecoder.createSource(cr,uri);
Drawable drawable = ImageDecoder.decodeDrawable(cr,source);
return drawable instanceof AnimatedImageDrawable;
}
EDIT:
for API 28 and before, we should use Movie class
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
ImageDecoder.Source source = ImageDecoder.createSource(file);
Drawable drawable = ImageDecoder.decodeDrawable(source);
return drawable instanceof AnimatedImageDrawable;
} else {
Movie movie = Movie.decodeStream(input);
return movie != null;
}
I hope that helps with your problem

How to print password protected PDF in android?

I am printing pdf using the below code which works fine for normal pdfs but crashes with password-protected pdf. is there any way by which we can print password-protected pdf or stop applications from crashing. application crashes even print() called inside a try-catch block.
Exception :
java.lang.RuntimeException:
at android.print.PrintManager$PrintDocumentAdapterDelegate$MyHandler.handleMessage (PrintManager.java:1103)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:246)
at android.app.ActivityThread.main (ActivityThread.java:8506)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1139)
code that causing Exception:
val printManager = this.getSystemService(Context.PRINT_SERVICE) as PrintManager
val jobName = this.getString(R.string.app_name) + " Document"
try{
printManager.print(jobName, pda, null)
}
catch(ex:RuntimeException)
{
Toast.makeText(this,"Can't print pdf file",Toast.LENGTH_SHORT).show()
}
PrintDocumentAdapter.kt
var pda: PrintDocumentAdapter = object : PrintDocumentAdapter() {
override fun onWrite(
pages: Array<PageRange>,
destination: ParcelFileDescriptor,
cancellationSignal: CancellationSignal,
callback: WriteResultCallback
) {
var input: InputStream? = null
var output: OutputStream? = null
try {
input = uri?.let { contentResolver.openInputStream(it) }
output = FileOutputStream(destination.fileDescriptor)
val buf = ByteArray(1024)
var bytesRead: Int
if (input != null) {
while (input.read(buf).also { bytesRead = it } > 0) {
output.write(buf, 0, bytesRead)
}
}
callback.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
} catch (ee: FileNotFoundException) {
//Code to Catch FileNotFoundException
} catch (e: Exception) {
//Code to Catch exception
} finally {
try {
input!!.close()
output!!.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
override fun onLayout(
oldAttributes: PrintAttributes,
newAttributes: PrintAttributes,
cancellationSignal: CancellationSignal,
callback: LayoutResultCallback,
extras: Bundle
) {
if (cancellationSignal.isCanceled) {
callback.onLayoutCancelled()
return
}
val pdi = PrintDocumentInfo.Builder("Name of file")
.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).build()
callback.onLayoutFinished(pdi, true)
}
}
OR if not possible then how to get password removed from pdf.
pdfView.fromAsset(String)
.pages(0, 2, 1, 3, 3, 3) // all pages are displayed by default
.enableSwipe(true) // allows to block changing pages using swipe
.swipeHorizontal(false)
.enableDoubletap(true)
.defaultPage(0)
// allows to draw something on the current page, usually visible in the middle of the screen
.onDraw(onDrawListener)
// allows to draw something on all pages, separately for every page. Called only for visible pages
.onDrawAll(onDrawListener)
.onLoad(onLoadCompleteListener) // called after document is loaded and starts to be rendered
.onPageChange(onPageChangeListener)
.onPageScroll(onPageScrollListener)
.onError(onErrorListener)
.onPageError(onPageErrorListener)
.onRender(onRenderListener) // called after document is rendered for the first time
// called on single tap, return true if handled, false to toggle scroll handle visibility
.onTap(onTapListener)
.onLongPress(onLongPressListener)
.enableAnnotationRendering(false) // render annotations (such as comments, colors or forms)
.password(null)
.scrollHandle(null)
.enableAntialiasing(true) // improve rendering a little bit on low-res screens
// spacing between pages in dp. To define spacing color, set view background
.spacing(0)
.autoSpacing(false) // add dynamic spacing to fit each page on its own on the screen
.linkHandler(DefaultLinkHandler)
.pageFitPolicy(FitPolicy.WIDTH) // mode to fit pages in the view
.fitEachPage(false) // fit each page to the view, else smaller pages are scaled relative to largest page.
.pageSnap(false) // snap pages to screen boundaries
.pageFling(false) // make a fling change only a single page like ViewPager
.nightMode(false) // toggle night mode
.load();
Have you tried updating the ".password" line?
You need not to generate pdf without password to print. as you said you are using barteksc:android-pdf-viewer for viewing pdfs which uses PDfium for rendering pdf which has a method to render bitmap from method.
void getBitmaps() {
ImageView iv = (ImageView) findViewById(R.id.imageView);
ParcelFileDescriptor fd = ...;
int pageNum = 0;
PdfiumCore pdfiumCore = new PdfiumCore(context);
try {
PdfDocument pdfDocument = pdfiumCore.newDocument(fd);
pdfiumCore.openPage(pdfDocument, pageNum);
int width = pdfiumCore.getPageWidthPoint(pdfDocument, pageNum);
int height = pdfiumCore.getPageHeightPoint(pdfDocument, pageNum);
// ARGB_8888 - best quality, high memory usage, higher possibility of OutOfMemoryError
// RGB_565 - little worse quality, twice less memory usage
Bitmap bitmap = Bitmap.createBitmap(width, height,
Bitmap.Config.RGB_565);
pdfiumCore.renderPageBitmap(pdfDocument, bitmap, pageNum, 0, 0,
width, height);
//if you need to render annotations and form fields, you can use
//the same method above adding 'true' as last param
iv.setImageBitmap(bitmap);
printInfo(pdfiumCore, pdfDocument);
pdfiumCore.closeDocument(pdfDocument); // important!
} catch(IOException ex) {
ex.printStackTrace();
}
}
store these bitmaps in arraylist and print them using android print framework

Image to text with same format - MLkit firebase

I'm developing an app to extract the text from Nutrition fact. By using MLkit firebase I achieve that, but I have one problem that the text doesn't show in the same format as in the image. Here is my code for text recognition.
// ----TextRecognizer START ---
BitmapDrawable bitmapDrawable = (BitmapDrawable) mPreview.getDrawable();
Bitmap bitmap = bitmapDrawable.getBitmap();
final FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
FirebaseVisionCloudTextRecognizerOptions options = new FirebaseVisionCloudTextRecognizerOptions.Builder()
.setLanguageHints(Arrays.asList("en", "ar"))
.build();
// [END set_detector_options_cloud]
// [START get_detector_cloud]
// Or, to change the default settings:
FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance()
.getCloudTextRecognizer(options);
// [END get_detector_cloud]
// [START run_detector_cloud]
Task<FirebaseVisionText> result2 = detector.processImage(image)
.addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() {
#Override
public void onSuccess(FirebaseVisionText result) {
// Task completed successfully
// [START_EXCLUDE]
// [START get_text_cloud]
StringBuilder sb = new StringBuilder();
for (FirebaseVisionText.TextBlock block : result.getTextBlocks()) {
if (result.getTextBlocks().size() == 0){
mResultEt.setText("NO Text Found");
}else {
Rect boundingBox = block.getBoundingBox();
Point[] cornerPoints = block.getCornerPoints();
String text = block.getText();
for (FirebaseVisionText.Line line: block.getLines()) {
sb.append(line.getText());
sb.append("\n");
for (FirebaseVisionText.Element element: line.getElements()) {
}
}
}
}
mResultEt.setText(sb);
// [END get_text_cloud]
// [END_EXCLUDE]
This is an image I want to extract the text from
this is the result but the format not as the image
I tried different solutions by adding a new line or tab but it's the same.
By the way, I want to use the numbers in the image to do some calculations.
If anyone can help me with it, I would appreciate.
ML Kit does not automatically detect the layout for now. But each detected text has its own coordinates. One solution is to use the text coordinates to detect decide the layout manually.
Say the 'Serving Size' and '1 Can' has the similar X-axle, then you may want to group them together.

Keep dimension of new image when replacing old image using docx4j

I need to add an image to my docx file. The image is a png image of a signature that is to placed behind text in the signature line of a certificate to be downloaded by the user as a docx, a pdf or jpg. The first problem I encountered is that you can only add inline image using the latest version of docx4j (v6.1.2) and creating an image Anchor is currently disabled (see BinaryPartAbstractImage.java: line 1029). That's a problem since the signature image is not inline, it supposed to appear behind the name on the signature line. Instead of inserting one myself, my workaround is to place a placeholder image:
These images are mapped as image1.png and image2.png, respectively, on /word/media directory of the docx uncompressed version. The program then replaces these with the name, position, and actual png of the signature every time a certificate is generated.
The problem is that the images are scaled the same dimension as the placeholder image, where in fact it should look like this:
How can I get to keep the image dimension of the image after replacing, or at least the aspect ratio? Here is how I replace the placeholder image with the new image:
File approveBySignatureImage = new File(...);
final String approvedByImageNodeId = "rId5";
replaceImageById(approvedByImageNodeId,
"image1.png", approveBySignatureImage);
This is the actual method where the replacing happens:
public void replaceImageById(String id, String placeholderImageName, File newImage) throws Exception {
Relationship rel = document.getMainDocumentPart().getRelationshipsPart().getRelationshipByID(id);
BinaryPartAbstractImage imagePart;
if(FilenameUtils.getExtension(placeholderImageName).toLowerCase() == ContentTypes.EXTENSION_BMP) {
imagePart = new ImageBmpPart(new PartName("/word/media/" + placeholderImageName));
}
else if([ContentTypes.EXTENSION_JPG_1, ContentTypes.EXTENSION_JPG_2].contains(FilenameUtils.getExtension(placeholderImageName).toLowerCase())) {
imagePart = new ImageJpegPart(new PartName("/word/media/" + placeholderImageName));
}
else if(FilenameUtils.getExtension(placeholderImageName).toLowerCase() == ContentTypes.EXTENSION_PNG) {
imagePart = new ImagePngPart(new PartName("/word/media/" + placeholderImageName));
}
InputStream stream = new FileInputStream(newImage);
imagePart.setBinaryData(stream);
if(FilenameUtils.getExtension(newImage.getName()).toLowerCase() == ContentTypes.EXTENSION_BMP) {
imagePart.setContentType(new ContentType(ContentTypes.IMAGE_BMP));
}
else if([ContentTypes.EXTENSION_JPG_1, ContentTypes.EXTENSION_JPG_2].contains(FilenameUtils.getExtension(newImage.getName()).toLowerCase())) {
imagePart.setContentType(new ContentType(ContentTypes.IMAGE_JPEG));
}
else if(FilenameUtils.getExtension(newImage.getName()).toLowerCase() == ContentTypes.EXTENSION_PNG) {
imagePart.setContentType(new ContentType(ContentTypes.IMAGE_PNG));
}
imagePart.setRelationshipType(Namespaces.IMAGE);
final String embedId = rel.getId();
rel = document.getMainDocumentPart().addTargetPart(imagePart);
rel.setId(embedId);
}
You'll need to set the dimensions (or possibly just remove what you have?) on your placeholder image.
For help in doing that:-
docx4j inspects the image to work that out at https://github.com/plutext/docx4j/blob/master/src/main/java/org/docx4j/openpackaging/parts/WordprocessingML/BinaryPartAbstractImage.java#L512 using org.apache.xmlgraphics ImageInfo.
See also CxCy:https://github.com/plutext/docx4j/blob/master/src/main/java/org/docx4j/openpackaging/parts/WordprocessingML/BinaryPartAbstractImage.java#L1164
https://github.com/plutext/docx4j/blob/master/src/main/java/org/docx4j/openpackaging/parts/WordprocessingML/BinaryPartAbstractImage.java#L815 shows scaling to maintain aspect ratio.

The best way to load pictures for photo gallery

I want to make some kind of a book(or some kind of a photo gallery) using jpg files of a scanned book.
the user gives the number of the page that he wants to go to , and clicks on the button to
see the page .
I need to know what is the best way to load the pictures.
i'm thinking of doing this for each page:
private ImageIcon image1= new ImageIcon ("1.jpg");
private ImageIcon image2 = new ImageIcon ("2.jpg");
....
and then put the pictures in an array and so on ...
but i got over 500 pictures and it is tedious to load pages like that .
so is there any other way?
Well, I can say the best way would be lazy loading plus pre-caching.
Lazy loading means you load the image only when the user needs it. For example:
img = 56; // suppose the user want to see page 56
if(images[img] != null) { // images is an array with the images
images[img] = new ImageIcon (img + ".jpg");
}
Besides, you can guest that when the user see a page they will see the next ones (pre-caching). So you can also load the following X pages.
PRELOAD = 10; // number of pages to preload
img = 56;
for(int i = 0; i < PRELOAD; i++) {
if(images[img+i] != null) {
images[img+i] = new ImageIcon ((img + i) + ".jpg");
}
}
Besides, it's you may think that in the beginning the user will always look at the firsts pages. So you can pre-load the first X pages in the start of your program.

Categories

Resources