Loading a bitmap with ImageDecoder from URI and adding text - java

In my activity, I want to modify an image to add a text on it.
The image is selected in the galery or taken with the camera and then stored in a file in a previous activity. Then the uri of that file is passed through extras.
Now I try to add a string on top of the image like so:
try {
modifyThePic(imageUri);
} catch (IOException e) {
e.printStackTrace();
}
here is the function's body:
public void modifyThePic(Uri imageUri) throws IOException {
ImageDecoder.Source source = ImageDecoder.createSource(this.getContentResolver(), imageUri);
Bitmap bitmap = ImageDecoder.decodeBitmap(source);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(10);
canvas.drawText("Some Text here", 0, 0, paint);
image.setImageBitmap(bitmap); //image is the imageView to control the result
}
The expected behaviour would be to display the image with "Some Text here" on top of it. But instead there is nothing displayed, however the app doesn't crash.
While debugging, I come across an error that appears between the
Bitmap bitmap = ImageDecoder.decodeBitmap(source);
and the
Canvas canvas = new Canvas(bitmap);
here is the error:
java.io.FileNotFoundException: No content provider: /storage/emulated/0/Android/data/com.emergence.pantherapp/files/Pictures/JPEG_20200829_181926_7510981182141824841.jpg
I suspect that I missuse the "ImageDecoder" as it's my first time using it. More precisely, I was not able to let the "decodeBitmap" method in onCreate, Android Studio was telling me that it could not be in the "main thread", I am not familiar at all with threading. Moving it in a dedicaded function fixed this but maybe am I supposed to do something else and it's the root of the problem.
My questions:
Am I using the right tools to modify the file and add the text on it ?
If yes, what do I do wrong ?
If no, what library/tools should I look into to do this task ?
Thank you,
EDIT: ADDITONAL ANSWER ELEMENTS
As both #blackapps and #rmunge pointed out, I was not getting a legit URI but instead a file path. The easy fix for my problem was to get the URI from the path using this code:
Uri realuri = Uri.fromFile(new File("insert path here")));
Additionaly to edit the bitmap, it must be made mutable which is covered here for example.
The final function to extract the bitmap from the URI and adding text on top of it is this one:
public void modifyThePic(Uri imageUri) throws IOException {
ContentResolver cr = this.getContentResolver();
InputStream input = cr.openInputStream(imageUri);
Bitmap bitmap = BitmapFactory.decodeStream(input).copy(Bitmap.Config.ARGB_8888,true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(300);
int xPos = (canvas.getWidth() / 2); //just some operations to center the text
int yPos = (int) ((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()) / 2)) ;
canvas.drawText("SOME TEXT TO TRY IT OUT", xPos, yPos, paint);
image.setImageBitmap(bitmap);
}

Seems your URI is wrong. It has to start with file:///

Related

Android: Display image from file in highest resolution

I have quite the annoying problem. I'm building an app where one can share photos. On the SurfaceView where you take the actual photo, the resolution is great. However, when I retrieve that image and display it in a ListView using Picasso, the resolution goes to crap. The pixelation is real. Is there anything that I'm doing horrendously wrong to cause this? The first code snippet below is where I actually save the photo, and the one below that is my getItemView() method in my adapter for the listview. Thanks in advance.
Note that the "photo" variable you see in my code is a Parse subclass I've created to make it easier working with data associated with each photo. I think you can safely ignore it.
EDIT:
SurfaceView of Camera:
Note that I attempt to set the camera parameters to the highest quality allowed. Unfortunately, when I LOG size.width and size.height, I can only get around 176x144. Is there a way to get a higher resolution for supported camera sizes itself?
camera.setDisplayOrientation(90);
Parameters parameters = camera.getParameters();
parameters.set("jpeg-quality", 70);
parameters.setPictureFormat(ImageFormat.JPEG);
List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
Size size = sizes.get(Integer.valueOf((sizes.size()-1)));
parameters.setPictureSize(size.width, size.height);
camera.setParameters(parameters);
camera.setDisplayOrientation(90);
List<Size> sizes2 = parameters.getSupportedPreviewSizes();
Size size2 = sizes.get(0);
parameters.setPreviewSize(size2.width, size2.height);
camera.setPreviewDisplay(holder);
camera.startPreview();
Saving the photo:
// Freeze camera
camera.stopPreview();
// Resize photo
Bitmap mealImage = BitmapFactory.decodeByteArray(data, 0, data.length);
Bitmap mealImageScaled = Bitmap.createScaledBitmap(mealImage, 640, 640, false);
// Override Android default landscape orientation and save portrait
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedScaledMealImage = Bitmap.createBitmap(mealImageScaled, 0,
0, mealImageScaled.getWidth(), mealImageScaled.getHeight(),
matrix, true);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
rotatedScaledMealImage.compress(Bitmap.CompressFormat.JPEG, 100, bos);
byte[] scaledData = bos.toByteArray();
// Save the scaled image to Parse with the date and time as its file name.
DateTime currentTime = new DateTime();
DateTimeFormatter fmt = DateTimeFormat.forPattern("HH MM SS");
photoFile = new ParseFile(currentTime.toString(fmt), scaledData);
photo.setPhotoFile(photoFile);
Displaying it:
final ParseImageView photoView = holder.photoView;
ParseFile photoFile = photo.getParseFile("photo");
Picasso.with(getContext())
.load(photoFile.getUrl())
.into(photoView, new Callback() {
#Override
public void onError() {
}
#Override
public void onSuccess() {
}
});
The problem is not with the Picasso
It because this line of code
parameters.set("jpeg-quality", 70);
and this
List<Size> sizes2 = parameters.getSupportedPreviewSizes();
Size size2 = sizes.get(0);
When you setup the camera you already turned down the quality to the 70% (because based on the Android Documentation the range of jpeq-quality is between 0-100)
And then you also need to check is the size of the camera is correct or not, because you are making assumption with that code
you can try this code to get the best preview size with your preffered width and height
private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters){
Camera.Size bestSize = null;
List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();
bestSize = sizeList.get(0);
for(int i = 1; i < sizeList.size(); i++){
if((sizeList.get(i).width * sizeList.get(i).height) >
(bestSize.width * bestSize.height)){
bestSize = sizeList.get(i);
}
}
return bestSize;
}
I hope this answer will help you, if you have another question about my answer you can try to ask me in the comment :)

Rotating Cursor or Cursor image Java

Okay. So I'm trying to rotate the cursor image or the cursor itself depending on it's position. I have tried using
Graphics2D g2d = (Graphics2D)g;
AffineTransform old = g2d.getTransform();
g2d.rotate(Math.toRadians(degrees));
Toolkit toolkit = Toolkit.getDefaultToolkit(); //Get the default toolkit
Image image = toolkit.getImage("pictures/skills/skill" +InfoReader.SkillData("CastImage") +".png"); //Load an image for the cursor
Cursor cursor = toolkit.createCustomCursor(image, new Point(0, 0), "Cursor");
setCursor(cursor);
g2d.setTransform(old);
So I was thinking that this should rotate the image, but g2d.rotate() doesen't seem to have any effect on cursor? I'm not 100% sure if it has affect on the image itself. Atleast the cursor image is what I want it to be though.
EDIT: Here's an example video :) (In my case, I just want to rotate it around a certain point which stays on the same spot all the time). https://www.youtube.com/watch?v=TQ71QXa-B-s
While searching and asking around for the solution of a similar problem I've found your question and also the answer to it. This is the code I use in my program and it works. Note that this method was designed to be called only once and calling it constantly might require optimization. Also I've learned AffineTransform today and might have made some mistakes(even though code works).
Basically I rotate an image, create a new image from it and set the new image as the cursor.
My "cursor.png" is in the data folder.
private void rotateCursor(double rotation) {
// Get the default toolkit
Toolkit toolkit = Toolkit.getDefaultToolkit();
// Load an image for the cursor
BufferedImage image = null;
try {
image = ImageIO.read(this.getClass().getResource("/data/cursor.png"));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
AffineTransform at = new AffineTransform();
// affineTransform applies the added transformations in the reverse order
// 3. translate it back to the center of the picture
at.translate(image.getWidth() / 2, image.getWidth() / 2);
at.rotate(rotation);//2- adds rotation to at (they are not degrees)
//1- translate the object so that you rotate it around the center
at.translate(-image.getWidth() / 2, -image.getHeight() / 2);
BufferedImage rotated = null; // creates new image that will be the transformed image
// makes this: at + image= rotated
AffineTransformOp affineTransformOp = new AffineTransformOp(at,
AffineTransformOp.TYPE_BILINEAR);
image2 = affineTransformOp.filter(image, rotated);
// Create the hotSpot for the cursor
Point hotSpot = new Point(10, 0); // click position of the cursor(ex: + shape's is middle)
Cursor cursor = toolkit.createCustomCursor(rotated, hotSpot, "cursor");
// Use the custom cursor
this.setCursor(cursor);
}
You can use window.getMousePosition().x; and window.getMousePosition().y; for getting mouse position if you are using a mouseListener.
You need to call rotateCursor() method with the correct double. How to calculate the correct double is something I can't help you with.
I hope it helps.
I've learned these from these sources:
storing transformed BufferedImage in Java
http://www.codebeach.com/2008/02/using-custom-cursors-in-java.html
Rotating BufferedImage instances
http://stumpygames.wordpress.com/2012/07/22/particles-tutorial-foundation/ (this tutorial also has a mouse listener)
It seems a bit confusing to me, what are you trying to rotate?
Lets imagine you have a BufferedImage object, you may get a Graphics2D object from it, and by operationg over it, you may get what you want.
java.awt.image.BufferedImage buffImage = null;
try {
java.io.InputStream imageStream =
MyClass.class.getResourceAsStream( "pictures/skills/skill" +InfoReader.SkillData("CastImage") +".png" );
//MyClass is anyclass that you use as relative path...
//use ClassLoader.getSystemClassLoader().getResourceAsStream( ... )
//for a absolute path
buffImage = javax.imageio.ImageIO.read( imageStream );
}
catch ( java.io.IOException | IllegalArgumentException ex ) {
//It may throw IllegalArgumentException if imageStream is null.
}
Graphics2D g2d = buffImage.createGraphics();
try {
AffineTransform old = g2d.getTransform();
g2d.rotate(Math.toRadians(degrees));
g2d.setTransform(old);
}finally {
g2d.dispose();
}
Toolkit toolkit = Toolkit.getDefaultToolkit(); //Get the default toolkit
Cursor cursor = toolkit.createCustomCursor(image, new Point(0, 0), "Cursor");
setCursor(cursor);
Now, if you intend to rotate it while you use it, I'm not sure how, but I hope I have helped a bit.
EDIT: Try to see this link, it might help you: https://www.google.com/#q=java+rotate+Cursor
EDIT 2: I see now what you want exaclty, I don't know how to help you, but try to see the link I gave you (yes it is from google). Even if you don't find much, it might help you in your quest.

Canvas bitmap to byte array and reading it back trouble

I have a Canvas I draw on, I'm trying to take the bitmap out, convert it to a byte array and save it serialized into a file. then later open, deserialize, and apply the bitmap back to the canvas. In the code below everything seems to work well except that when applying the bitmap to canvas nothing appears. can someone please show me where I'm going wrong.
public byte[] getCanvasData(){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
mBitmap.compress(CompressFormat.PNG, 0, bos);
byte[] bitmapdata = bos.toByteArray();
return bitmapdata;
}
public void setCanvasData(byte[] canvasData, int w, int h){
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mBitmap.eraseColor(0x00000000);
mCanvas = new Canvas(mBitmap);
mCanvas.drawBitmap(BitmapFactory.decodeByteArray(canvasData , 0, canvasData.length).copy(Bitmap.Config.ARGB_8888, true), 0, 0, null);
}
ADDED SOME EXTRA CODE TO POSSIBLY HELP A LITTLE
public void readInSerialisable() throws IOException
{
FileInputStream fileIn = new FileInputStream("/sdcard/theBKup.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
try
{
BookData book = (BookData) in.readObject();
pages.clear();
canvasContainer.removeAllViews();
for (int i = 0; i < book.getBook().size(); i++){
Log.d("CREATION", "LOADING PAGE " + i);
pages.add(new Canvas2(context, book.getPageAt(i), canvasContainer.getWidth(), canvasContainer.getHeight()));
}
canvasContainer.addView(pages.get(page), new AbsoluteLayout.LayoutParams(AbsoluteLayout.LayoutParams.FILL_PARENT, AbsoluteLayout.LayoutParams.FILL_PARENT, 0, 0));
updatePagination();
Log.d("CREATION", "Updated Pagination");
}
catch (Exception exc)
{
System.out.println("didnt work");
exc.printStackTrace();
}
}
BookData - Serializable class containing all my data, simple gets/sets in there
onDraw Method
#Override
protected void onDraw(Canvas canvas) {
Log.d("DRAWING", "WE ARE DRAWING");
canvas.drawColor(0x00AAAAAA); //MAKE CANVAS TRANSPARENT
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
I would do the following 2 tests.
Log some of the byte stream to make sure that it was loaded correctly. Something like Log.v(canvasData[0]+canvasData[1]);, or put a break point there, or something just to make sure the data is correct.
Draw a bitmap that you know is valid, using the same code, and see if it appears correctly.
I'm not sure exactly what's going on, but I strongly suspect one of the following.
The byte stream is not being read in correctly.
The bitmap is not being updated to the screen, or is using a trivially small size.
In the event that your byte stream data has something, then you will want to take a look at the Canvas documentation. Specifically, look at the following bit.
In order to see a Canvas, it has to be put on to a view. Once it is on a view, the onDraw() command must be called for it to be visible. I would make sure that you are in fact doing an onDraw(), and that the Canvas is associated with the View correctly. If you are using an onDraw() already, please post the bits of code associated with it.

Generate a image with custom text in Android

I'm trying to make an app for create custom cards. I'd like to add some text over a custom background (a jpg image).
What is the best way of doing it? I'd need to show the user a preview of the card before send it to the server.
Thanks
Use below code to achieve your requirement
Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.yourimage); // the original file yourimage.jpg i added in resources
Bitmap dest = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
String yourText = "My custom Text adding to Image";
Canvas cs = new Canvas(dest);
Paint tPaint = new Paint();
tPaint.setTextSize(35);
tPaint.setColor(Color.BLUE);
tPaint.setStyle(Style.FILL);
cs.drawBitmap(src, 0f, 0f, null);
float height = tPaint.measureText("yY");
float width = tPaint.measureText(yourText);
float x_coord = (src.getWidth() - width)/2;
cs.drawText(yourText, x_coord, height+15f, tPaint); // 15f is to put space between top edge and the text, if you want to change it, you can
try {
dest.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(new File("/sdcard/ImageAfterAddingText.jpg")));
// dest is Bitmap, if you want to preview the final image, you can display it on screen also before saving
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
You have to use below permission in manifest file.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
For my device the path is /sdcard to access external SD card, it may vary for other devices. Some devices may have /mnt/sdcard may be it is for internal sd cards. Just check it while before using this code.
Actually I wrote the above code for some other question, which required time stamp on photo after captured from camera. I gave you the same solution with a little modifications for your specific requirement.
I hope you can understand this. If you have any doubts regarding code feel free to ask.
I am not sure this is the best solution, but it might help you.
Step1: Create a relative layout (or any other layout) and set your image as its background.
Step2: Now add a TextView with width and height as match_parent and gravity set as top|center_horizontal.
Step3: Now add another button or any other layout control which will trigger the user confirmation. (You should place this control outside the Relative layout).
Step4: If user has confirmed the image then you can take screenshot of your relative layout via following code:
v1.setDrawingCacheEnabled(true); //v1 is the object of your Relative layout
Bitmap bm = v1.getDrawingCache();
if (bm != null) {
//TODO: write the code for saving the image.
Toast toast = Toast.makeText(YourActivity.this, "image saved",
Toast.LENGTH_LONG);
toast.show();
} else {
Toast toast = Toast.makeText(YourActivity.this,
"No image saved.", Toast.LENGTH_LONG);
toast.show();
}

Java BufferedImage alternatives

I am trying to implement a simple class that will allow a user to crop an image to be used for their profile picture. This is a java web application.
I have done some searching and found that java.awt has a BufferedImage class, and this appears (at first glance) to be perfect for what I need. However, it seems that there is a bug in this (or perhaps java, as I have seen suggested) that means that the cropping does not always work correctly.
Here is the code I am using to try to crop my image:
BufferedImage profileImage = getProfileImage(form, modelMap);
if (profileImage != null) {
BufferedImage croppedImage = profileImage
.getSubimage(form.getStartX(), form.getStartY(), form.getWidth(), form.getHeight());
System.err.println(form.getStartX());
System.err.println(form.getStartY());
File finalProfileImage = new File(form.getProfileImage());
try {
String imageType = getImageType(form.getProfileImage());
ImageIO.write(croppedImage, imageType, finalProfileImage);
}
catch (Exception e) {
logger.error("Unable to write cropped image", e);
}
}
return modelAndView;
}
protected BufferedImage getProfileImage(CropImageForm form, Map<String, Object> modelMap) {
String profileImageFileName = form.getProfileImage();
if (validImage(profileImageFileName) && imageExists(profileImageFileName)) {
BufferedImage image = null;
try {
image = getCroppableImage(form, ImageIO.read(new File(profileImageFileName)), modelMap);
}
catch (IOException e) {
logger.error("Unable to crop image, could not read profile image: [" + profileImageFileName + "]");
modelMap.put("errorMessage", "Unable to crop image. Please try again");
return null;
}
return image;
}
modelMap.put("errorMessage", "Unable to crop image. Please try again.");
return null;
}
private boolean imageExists(String profileImageFileName) {
return new File(profileImageFileName).exists();
}
private BufferedImage getCroppableImage(CropImageForm form, BufferedImage image, Map<String, Object> modelMap) {
int cropHeight = form.getHeight();
int cropWidth = form.getWidth();
if (cropHeight <= image.getHeight() && cropWidth <= image.getWidth()) {
return image;
}
modelMap.put("errorMessage", "Unable to crop image. Crop size larger than image.");
return null;
}
private boolean validImage(String profileImageFileName) {
String extension = getImageType(profileImageFileName);
return (extension.equals("jpg") || extension.equals("gif") || extension.equals("png"));
}
private String getImageType(String profileImageFileName) {
int indexOfSeparator = profileImageFileName.lastIndexOf(".");
return profileImageFileName.substring(indexOfSeparator + 1);
}
The form referred to in this code snippet is a simple POJO which contains integer values of the upper left corner to start cropping (startX and startY) and the width and height to make the new image.
What I end up with, however, is a cropped image that always starts at 0,0 rather than the startX and startY position. I have inspected the code to make sure the proper values are being passed in to the getSubimage method, and they appear to be.
Are there simple alternatives to using BufferedImage for cropping an image. I have taken a brief look at JAI. I would rather add a jar to my application than update the jdk installed on all of the production boxes, as well as any development/testing servers and local workstations.
My criteria for selecting an alternative are:
1) simple to use to crop an image as this is all I will be using it for
2) if not built into java or spring, the jar should be small and easily deployable in a web-app
Any suggestions?
Note: The comment above that there is an issue with bufferedImage or Java was something I saw in this posting: Guidance on the BufferedImage.getSubimage(int x, int y, int w, int h) method?
I have used getSubimage() numerous times before without any problems. Have you added a System.out.println(form.getStartX() + " " + form.getStartY()) before that call to make sure they're not both 0?
Also, are you at least getting an image that is form.getWidth() x form.getHeight()?
Do make sure you are not modifying/disposing profileImage in any way since the returned BufferedImage shares the same data array as the parent.
The best way is to just simply draw it across if you want a completely new and independent BufferedImage:
BufferedImage croppedImage = new BufferedImage(form.getWidth(),form.getHeight(),BufferedImage.TYPE_INT_ARGB);
Graphics g = croppedImage.getGraphics();
g.drawImage(profileImage,0,0,form.getWidth(),form.getHeight(),form.getStartX(),form.getStartY(),form.getWidth(),form.getHeight(),null);
g.dispose();
You can do it in this manner as well (code is not 100% tested as I adopted for example from an existing app i did):
import javax.imageio.*;
import java.awt.image.*;
import java.awt.geom.*;
...
BufferedImage img = ImageIO.read(imageStream);
...
/*
* w = image width, h = image height, l = crop left, t = crop top
*/
ColorModel dstCM = img.getColorModel();
BufferedImage dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(w, h), dstCM.isAlphaPremultiplied(), null);
Graphics2D g = dst.createGraphics();
g.drawRenderedImage(img, AffineTransform.getTranslateInstance(-l,-t));
g.dispose();
java.io.File outputfile = new java.io.File(sessionScope.get('absolutePath') + java.io.File.separator + sessionScope.get('lastUpload'));
ImageIO.write(dst, 'png', outputfile);
Thanks for all who replied. It turns out that the problem was not in the cropping code at all.
When I displayed the image to be cropped, I resized it to fit into my layout nicely, then used a javascript cropping tool to figure out the coordinates to crop.
Since I had resized my image, but didn't take the resizing into account when I was determining the cropping coordinates, I ended up with coordinates that appeared to coincide with the top left corner.
I have changed the display to no longer resize the image, and now cropping is working beautifully.

Categories