I'm trying to get the text input from the user and draw it on the image using Canvas but the image is saved without what was supposed to be drawn. Right now, I'm just trying to get the text on the Image before I worry about the font, colour, styles, etc.
This is my code:
public void createBitmapAndSave(ImageView img){
BitmapDrawable bitmapDrawable = ((BitmapDrawable) img.getDrawable());
Bitmap bitmap = bitmapDrawable.getBitmap();
Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(mutableBitmap);
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(200);
paint.setStyle(Paint.Style.FILL);
paint.setShadowLayer(10f, 10f, 10f, Color.BLACK);
String topText = topTextView.getText().toString();
String bottomText = bottomTextView.getText().toString();
canvas.drawText(topText, 0, 0, paint);
canvas.drawText(bottomText, 50, 50, paint);
File file;
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
file = new File(path + "/SimpliMeme/" + timeStamp + "-" + counter + ".jpg");
file.getParentFile().mkdir();
try{
OutputStream stream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,stream);
stream.flush();
stream.close();
Toast.makeText(getContext(), "Meme Saved", Toast.LENGTH_SHORT).show();
}
catch (IOException e){ e.printStackTrace();}
Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
Objects.requireNonNull(getContext()).sendBroadcast(mediaScanIntent);
counter++;
}
At the moment, I only have the 2 .drawText() implementations based on the examples that I've seen in other SO posts. My assumption is that the text isn't visible and no changes are made to the image because I haven't provided the paint object with any attributes.
The main issue why you see no changes is that you make changes to mutableBitmap but save the original bitmap to disk.
This can be avoided by joining the first two (or even three) statements together:
final Bitmap bitmap = bitmapDrawable.getBitmap()
.copy(Bitmap.Config.ARGB_8888, true);
You didn't need the orginal bitmap anywhere else, this effectively prevents you from making the mistake. Don't do what you don't need to do.
Some tips:
Always be explicit when drawing. Specify the color, specify the font. You can't trust default values. (At least I'm never sure about the values. Is the default color black or transparent?)
If you want to be asolutely sure about the font, bundle it with your app or use downloadable fonts. Some platforms allow the user to change the default font to something crazy.
If you ever want to draw multiline text look into StaticLayout.
Make sure your app works on Android 7 and above. Sending intents with file Uri outside your app is prohibited.
Related
I am making an application where I want to take a screenshot. In foreground I am running some videos which are looping, also I have a background picture set. The problem is, when I take the screenshot, I get the background picture and not the picture of the video that runs.
try {
// image naming and path to include sd card appending name you choose for file
String mPath = Environment.getExternalStorageDirectory().toString() + "/app/" + fname + ".jpg";
// create bitmap screen capture
View v1 = getWindow().getDecorView().getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(0, Mode.CLEAR);
File imageFile = new File(mPath);
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 90;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
Log.d("MainActivity","TakeScreenshot SUCCESS");
//openScreenshot(imageFile);
} catch (Throwable e) {
// Several error may come out with file handling or OOM
Log.d("MainActivity","TakeScreenshot ERROR ");
e.printStackTrace();
}
Does anybody know how to take a snapshot of the video running on screen?
I'm working with GeoTiff/PNG files too large for handling as a whole in my code.
Is there any possibility to decode specific areas (e.g. given by two x,y coordinates) of a file in bitmapfactory? Haven't found anything looking similar at http://developer.android.com/reference/android/graphics/BitmapFactory.html(Android's developer reference).
Thanks!
With kcoppock's hint I've set up the following solution.
Though I'm wondering why rect needs to be initialized by Rect(left, bottom, right, top) instead of Rect(left, top, right, bottom)...
Example call:
Bitmap myBitmap = loadBitmapRegion(context, R.drawable.heightmap,
0.08f, 0.32f, 0.13f, 0.27f);
Function:
public static Bitmap loadBitmapRegion(
Context context, int resourceID,
float regionLeft, float regionTop,
float regionRight, float regionBottom) {
// Get input stream for resource
InputStream is = context.getResources().openRawResource(resourceID);
// Set options
BitmapFactory.Options opt = new BitmapFactory.Options();
//opt.inPreferredConfig = Bitmap.Config.ARGB_8888; //standard
// Create decoder
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(is, false);
} catch (IOException e) {
e.printStackTrace();
}
// Get resource dimensions
int h = decoder.getHeight();
int w = decoder.getWidth();
// Set region to decode
Rect region = new Rect(
Math.round(regionLeft*w), Math.round(regionBottom*h),
Math.round(regionRight*w), Math.round(regionTop*h));
// Return bitmap
return decoder.decodeRegion(region, opt);
}
You should look into BitmapRegionDecoder. It seems to describe exactly the use case that you are looking for.
I don't know exactly what you mean by "Decode specific areas" but if by decoding you mean, to actually "copy" certain areas of a bitmap, what you can do is make use of canvas in order to get it as shown below:
Bitmap bmpWithArea = Bitmap.createBitmap(widthDesired, heightDesired, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmpWithArea);
Rect area = new Rect(arealeft, areatop, arearight, areabottom);
Rect actualSize = new Rect(0, 0, widthDesired, heightDesired);
canvas.drawBitmap(bitmapWithAreaYouWantToGet, area, actual, paintIfAny);
//And done, starting from this line "bmpWithArea" has the bmp that you wanted, you can assign it to ImageView and use it as regular bmp...
Hope this helps...
Regards!
SOLUTION
Thanks to #ChandraSekhar's suggestions the issue was that I was passing in an Immutable Bitmap to the canvas constructor. The solution is to create a copy of it when using BitmapFactory.decodeFile();
Bitmap bmp = BitmapFactory.decodeFile(imageURL).copy(Bitmap.Config.ARGB_8888, true);
So I have a bitmap that I am using bitmapFactory.decodeFile() for and this works. I am able to create the bitmap, then I need to create a canvas and this is where things get weird.
Here's the flow of what is happening.
I capture an image, then pass it to functionA that sizes it, and saves it out and returns its file path. ( I am using Phonegap Cordova )
I then pass that URL back to my java and use the previously saved image and manipulate it in functionB
CODE IN QUESTION:
// GET URL TO IMAGE
final JSONObject options = optionsArr.optJSONObject(0);
String imageURL = options.optString("image");
// create image bitmap
Bitmap bmp = BitmapFactory.decodeFile(imageURL);
bmp = Bitmap.createBitmap(bmp,0,0,655,655);
/* Everything works fine until this point */
// create image canvas
Canvas canvas = new Canvas(bmp);
Bitmap one = Bitmap.createBitmap(bmp);
canvas.drawBitmap(one,0,0,null);
I receive no errors, it just hangs. Here's the kick in the pants - if I run another function say functionB first that one works but the other doesn't.
I thought maybe I needed to flush and close my first FileOutputStream, but that didn't seem to have any effect. I've tried different variable names for all elements, bitmaps, canvas, and fileoutputstreams.
here is an example of the full function...
NOTE: Because I am using phonegap / cordova I am returning a string
public String none(JSONArray optionsArr) {
// SET FILE PATH
String filePath = "";
File path = new File(Environment.getExternalStorageDirectory()+"/myApp/");
// TMP.jpg is where we store our temporary version of the image
File NewFilePath = new File(path, "tmp_NBB.jpg");
// CREATE FOLDERS IF NEEDED
try{
boolean success = false;
if(!path.exists()){
success = path.mkdir();
}
if (!success){
Log.d("NONE","Folder not created.");
}
else{
Log.d("NONE","Folder created!");
}
}
catch (Exception e){
e.printStackTrace();
}
// GET URL TO IMAGE
final JSONObject options = optionsArr.optJSONObject(0);
String imageURL = options.optString("image");
// create image bitmap
Bitmap bmp = BitmapFactory.decodeFile(imageURL);
bmp = Bitmap.createBitmap(bmp,0,0,655,655);
// create image canvas
Canvas canvas = new Canvas(bmp);
Bitmap none = Bitmap.createBitmap(bmp);
canvas.drawBitmap(none,0,0,null);
// SAVE IMAGE
try {
// OUTPUT STREAM
FileOutputStream out = new FileOutputStream(NewFilePath);
none.compress(Bitmap.CompressFormat.JPEG, 100, out);
// GET FILE PATH
Uri uri = Uri.fromFile(NewFilePath);
filePath = uri.toString();
try{
out.flush();
out.close();
// RETURN FILE PATH
return filePath;
}
catch (Exception e){
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
return filePath;
}
Like I said this works for the first image, but when I attempt to open this image again, based on the returned filepath it chunks out at the create canvas line.
edit: The image path I am using looks like this:
/mtn/sdcard/myApp/tmp.jpg
thoughts?
Bitmap one = Bitmap.createBitmap(bmp);
In the above code bmp is a Bitmap and you are creating another Bitmap object one from bmp.
Remove that line and try by changing
canvas.drawBitmap(one,0,0,null);
to
canvas.drawBitmap(bmp,0,0,null);
Are you sure, the device on which you are running supports image size:655x655? Does bitmap get created?
I have two images that I want to merge into one. (Eg "House.png" on top of "street.png")
How do i achieve this in Android? I just want to merge the images and export them to a file.
This example Sets the image to an ImageView but i wish to export it.
This other example does not work in Android since the classes are not available.
I'd try something like:
public static Bitmap mergeImages(Bitmap bottomImage, Bitmap topImage) {
final Bitmap output = Bitmap.createBitmap(bottomImage.getWidth(), bottomImage
.getHeight(), Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(bottomImage, 0, 0, paint);
canvas.drawBitmap(topImage, 0, 0, paint);
return output;
}
(not tested, I just wrote it here, might be some simple errors in there)
Basically what you do is create a 3rd empty bitmap, draw the bottom image on it and then draw the top image over it.
As for saving to a file, here are a few examples: Save bitmap to location
You can do like this...............
public Bitmap Overlay(Bitmap Bitmap1, Resources paramResources, Bitmap Bitmap2, int alpha)
{
Bitmap bmp1 = Bitmap.createScaledBitmap(Bitmap2, Bitmap1.getWidth(), Bitmap1.getHeight(), true);
Bitmap bmp2 = Bitmap.createBitmap(Bitmap1.getWidth(), Bitmap1.getHeight(), Bitmap1.getConfig());
Paint localPaint = new Paint();
localPaint.setAlpha(alpha);
Canvas localCanvas = new Canvas(bmp2);
Matrix localMatrix = new Matrix();
localCanvas.drawBitmap(Bitmap1, localMatrix, null);
localCanvas.drawBitmap(bmp1, localMatrix, localPaint);
bmp1.recycle();
System.gc();
return bmp2;
}
So I have been posting all over and have yet to get a solid answer:
I have created an image resizing class, with a crop method. The cropping works great. The issue that I am having is the background color that I specify in the drawImage function of Graphics is not working correctly. It defaults to black as the background regardless of what I supply (in this case Color.WHITE).
Also, the overlaying image or top most image (comes from a file) is being inverted (I think it is) or otherwise discolored. Just so you can conceptualize this a little bit better, I am taking a jpeg and overlaying it on top of a new BufferedImage, the new buffered image's background is not being set. Here is the code below that I am working with:
public void Crop(int Height, int Width, int SourceX, int SourceY) throws Exception {
//output height and width
int OutputWidth = this.OutputImage.getWidth();
int OutputHeight = this.OutputImage.getHeight();
//create output streams
ByteArrayOutputStream MyByteArrayOutputStream = new ByteArrayOutputStream();
MemoryCacheImageOutputStream MyMemoryCacheImageOutputStream = new MemoryCacheImageOutputStream(MyByteArrayOutputStream);
//Create a new BufferedImage
BufferedImage NewImage = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
Graphics MyGraphics = NewImage.createGraphics();
MyGraphics.drawImage(this.OutputImage, -SourceX, -SourceY, OutputWidth, OutputHeight, Color.WHITE, null);
// Get Writer and set compression
Iterator MyIterator = ImageIO.getImageWritersByFormatName("png");
if (MyIterator.hasNext()) {
//get image writer
ImageWriter MyImageWriter = (ImageWriter)MyIterator.next();
//get params
ImageWriteParam MyImageWriteParam = MyImageWriter.getDefaultWriteParam();
//set outputstream
MyImageWriter.setOutput(MyMemoryCacheImageOutputStream);
//create new ioimage
IIOImage MyIIOImage = new IIOImage(NewImage, null, null);
//write new image
MyImageWriter.write(null, MyIIOImage, MyImageWriteParam);
}
//convert output stream back to inputstream
ByteArrayInputStream MyByteArrayInputStream = new ByteArrayInputStream(MyByteArrayOutputStream.toByteArray());
MemoryCacheImageInputStream MyMemoryCacheImageInputStream = new MemoryCacheImageInputStream(MyByteArrayInputStream);
//resassign as a buffered image
this.OutputImage = ImageIO.read(MyMemoryCacheImageInputStream);
}
Can you isolate whether it's the Graphics methods or the ImageIO methods that are mangling your image? It looks like you could test this by short-circuiting the entire ImageIO process and simply assigning
this.OutputImage = NewImage;
For that matter, I assume there's something gained by the ImageIO operations? As the sample is written, it appears to be (ideally) a no-op.
Also, you don't dispose your Graphics2D before you begin the ImageIO process. It often doesn't make a difference, but you don't want to assume that.
On the overlay color distortion problem, make sure your graphics context is in paint mode and not xor mode. (Graphics.setPaintMode()). Otherwise the color bits are XOR'd together.