I need to create a completely round image in Android. I've got the following code which works in newer version of Android, but in older versions (2.3), it is just a black circle:
Bitmap avatar = null;
if (avatarUrl == null || avatarUrl.equals("")) {
avatar = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_no_avatar);
} else {
avatar = HttpClientHelper.downloadImage(avatarUrl);
}
if (avatar == null) {
// No avatar? Load the default one and use it instead
avatar = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_no_avatar);
}
float width = 50; // avatar.getWidth();
float height = 50; // avatar.getHeight();
float ratio = (float)avatar.getWidth() / (float)avatar.getHeight();
// Scale the avatar to the area that it needs to fit into
avatar = Bitmap.createScaledBitmap(avatar, (int)(width * ratio), (int)height, true);
// Turn the avatar into a round image
Bitmap circleBitmap = Bitmap.createBitmap((int)width, (int)height, Bitmap.Config.ARGB_8888);
BitmapShader shader = new BitmapShader(avatar, TileMode.CLAMP, TileMode.CLAMP);
Paint paint = new Paint();
paint.setShader(shader);
RectF rect = new RectF(0.0f, 0.0f, width, height);
Canvas c = new Canvas(circleBitmap);
c.drawCircle(width / 2, height / 2, (width / 2) - 1, paint);
Is there something different I should do with 2.3 to make this work?
Use this
float width = 50; // avatar.getWidth();
float height = 50; // avatar.getHeight();
float radius = 45;
Bitmap bmpBG = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmpBG);
//YOU CAN ADD SHADER FOR EFFECTS
Paint p = new Paint();
p.setAntiAlias(true);
p.setDither(true);
p.setColor(Color.RED);//SET YOUR COLOR HERE
c.drawCircle(width/ 2, height/ 2,
radius , p);
//NOW USE bmpBG
This must be a bug in 2.3. I was previously setting the round bitmap as the ImageView background which caused the black circle. When I set it as the foreground, the problem went away.
Again, this only happens with 2.3. After 3.0 this problem no longer exists.
Related
When drawing the text though, it gets blurred and not that crisp like when using below code. The only code I've used for the text painting on image can some one help me to solve this issue
code
public static Bitmap drawMultilineTextToBitmap(Context gContext, Bitmap bitmap, String gText) {
// prepare canvas
Resources resources = gContext.getResources();
float scale = resources.getDisplayMetrics().density;
//Bitmap bitmap = BitmapFactory.decodeResource(resources, gResId);
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
// set default bitmap config if none
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
// so we need to convert it to mutable one
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
TextPaint paint = new TextPaint(Paint.LINEAR_TEXT_FLAG|Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
paint.setColor(Color.rgb(0, 0, 0));
paint.setFakeBoldText(true);
paint.setTextSize(10);
int textWidth = canvas.getWidth() - (int) (16 * scale);
// init StaticLayout for text
StaticLayout textLayout = new StaticLayout(gText, paint,
canvas.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f,
false);
// get height of multiline text
int textHeight = textLayout.getHeight();
// get position of text's top left corner
float x = (bitmap.getWidth() - textWidth);
float y = (bitmap.getHeight() - textHeight);
// draw text to the Canvas Left
canvas.save();
//canvas.translate(x, y);
canvas.translate((canvas.getWidth() / 2) - (textLayout.getWidth() / 2), y);
textLayout.draw(canvas);
canvas.restore();
return bitmap;
}
image
I guess here:
paint.setAntiAlias(true);
should be false. And two more methods which may relate to this issue:
paint.setAntiAlias(false);
paint.setFilterBitmap(false);
paint.setDither(true);
Im quite new to Android so forgive me if I have missed something.
I've got the following code which displays a custom marker on maps. This custom marker also has some text on it. It works well up to the point where I want to resize the marker by a certain amount when the text is longer.
The code I originally had for the custom marker was,
private Bitmap drawTextToBitmap(final int mResId, final String mText) {
Resources resources = getResources();
// Get the screen's density scale
float scale = resources.getDisplayMetrics().density;
Bitmap bitmap = BitmapFactory.decodeResource(resources, mResId);
Bitmap.Config bitmapConfig = bitmap.getConfig();
if ( bitmapConfig == null ) {
bitmapConfig = Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// Set font color
paint.setColor(Color.WHITE);
// Set font size and scale it
paint.setTextSize((int) (14 * scale));
// Set shadow width
paint.setShadowLayer(1f, 0f, 1f, Color.BLACK);
// Set anti-aliasing
paint.setAntiAlias(true);
// Make font bold
paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
Rect bounds = new Rect();
paint.getTextBounds(mText, 0, mText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width())/2;
int y = ((bitmap.getHeight() + bounds.height())/2)-25;
canvas.drawText(mText, x, y, paint);
return bitmap;
}
How can I resize this bitmap and also increase the font size accordingly without loosing any resolution ? Unfortunately I cant give screenshots due to licensing/permission issues.
Found the solution which was to add the following before creating the canvas.
int newWidth = (int) (origBitmap.getWidth() * scalingFactor);
int newHeight = (int) (origBitmap.getHeight() * scalingFactor);
Bitmap bitmap = Bitmap.createScaledBitmap(origBitmap, newWidth, newHeight, true);
This will scale the bitmap accordingly. I can similarly scale the font using the same scalingFactor.
I have an image with frames and I need to add a watermark effect. How might I do this?
I found great tutorial on Android Image Processing here.
public static Bitmap mark(Bitmap src, String watermark, Point location, Color color, int alpha, int size, boolean underline) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
paint.setColor(color);
paint.setAlpha(alpha);
paint.setTextSize(size);
paint.setAntiAlias(true);
paint.setUnderlineText(underline);
canvas.drawText(watermark, location.x, location.y, paint);
return result;
}
Thanks to Pete Houston who shares such useful tutorial on basic image processing.
For others reference, if you want to add the logo of your application (which is in your drawable folder(s)) on top of image use following method:
private Bitmap addWaterMark(Bitmap src) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Bitmap waterMark = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.logo);
canvas.drawBitmap(waterMark, 0, 0, null);
return result;
}
If someone is still searching for this, I found a good solution here
It adds a watermark to the bottom right portion and scales it according to the source image which was exactly what I was looking for.
/**
* Embeds an image watermark over a source image to produce
* a watermarked one.
* #param source The source image where watermark should be placed
* #param watermark Watermark image to place
* #param ratio A float value < 1 to give the ratio of watermark's height to image's height,
* try changing this from 0.20 to 0.60 to obtain right results
*/
public static Bitmap addWatermark(Bitmap source, Bitmap watermark, float ratio) {
Canvas canvas;
Paint paint;
Bitmap bmp;
Matrix matrix;
RectF r;
int width, height;
float scale;
width = source.getWidth();
height = source.getHeight();
// Create the new bitmap
bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);
// Copy the original bitmap into the new one
canvas = new Canvas(bmp);
canvas.drawBitmap(source, 0, 0, paint);
// Scale the watermark to be approximately to the ratio given of the source image height
scale = (float) (((float) height * ratio) / (float) watermark.getHeight());
// Create the matrix
matrix = new Matrix();
matrix.postScale(scale, scale);
// Determine the post-scaled size of the watermark
r = new RectF(0, 0, watermark.getWidth(), watermark.getHeight());
matrix.mapRect(r);
// Move the watermark to the bottom right corner
matrix.postTranslate(width - r.width(), height - r.height());
// Draw the watermark
canvas.drawBitmap(watermark, matrix, paint);
return bmp;
}
And it is well commented which is what is a huge plus!
It seems you are looking for a waterrippleeffect as this one. Checkout the complete source code. Also check the screenshot how does the effect look like.
In Kotlin:
Note: Its just modified code of above answers
private fun mark(src: Bitmap, watermark: String): Bitmap {
val w = src.width
val h = src.height
val result = Bitmap.createBitmap(w, h, src.config)
val canvas = Canvas(result)
canvas.drawBitmap(src, 0f, 0f, null)
val paint = Paint()
paint.color = Color.RED
paint.textSize = 10f
paint.isAntiAlias = true
paint.isUnderlineText = true
canvas.drawText(watermark, 20f, 25f, paint)
return result
}
val imageBitmap = mark(yourBitmap, "Your Text")
binding.meetProofImageView.setImageBitmap(imageBitmap)
You can use androidWM to add a watermark into your image, even with invisible watermarks:
add dependence:
dependencies {
...
implementation 'com.huangyz0918:androidwm:0.2.3'
...
}
and java code:
WatermarkText watermarkText = new WatermarkText(“Hello World”)
.setPositionX(0.5)
.setPositionY(0.5)
.setTextAlpha(100)
.setTextColor(Color.WHITE)
.setTextFont(R.font.champagne)
.setTextShadow(0.1f, 5, 5, Color.BLUE);
WatermarkBuilder.create(this, backgroundBitmap)
.loadWatermarkText(watermarkText)
.getWatermark()
.setToImageView(backgroundView);
You can easily add an image type watermark or a text watermark like this, and the library size is smaller than 30Kb.
I tried a few libraries mentioned in other posts, like this, but unfortunately it is missing, and not downloadable now. So I followed AndroidLearner 's answer above, but after tweaking the code a little bit, for those of you who are having trouble rotating the watermark, and what values are valid for the various methods of Paint class, so that the text shows rotated at an angle(like most of the company watermarks do), you can use the below code.
Note that, w and h are the screen width and height respectively, which you can calculate easily, there are tons of ways you can find on stackoverflow only.
public static Bitmap waterMarkBitmap(Bitmap src, String watermark) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap mutableBitmap = Utils.getMutableBitmap(src);
Bitmap result = Bitmap.createBitmap(w, h, mutableBitmap.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0f, 0f, null);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setTextSize(92f);
paint.setAntiAlias(true);
paint.setAlpha(70); // accepts value between 0 to 255, 0 means 100% transparent, 255 means 100% opaque.
paint.setUnderlineText(false);
canvas.rotate(45, w / 10f, h / 4f);
canvas.drawText(watermark, w / 10f, h / 4f, paint);
canvas.rotate(-45, w / 10f, h / 4f);
return result;
}
It rotates the text watermark by 45 degrees, and places it at the centre of the bitmap.
Also note that, in case you are not able to get watermark, it might be the case that the bitmap you are using as source is immutable. For this worst case scenario, you can use below method to create a mutable bitmap from an immutable one.
public static Bitmap getMutableBitmap(Bitmap immutableBitmap) {
if (immutableBitmap.isMutable()) {
return immutableBitmap;
}
Bitmap workingBitmap = Bitmap.createBitmap(immutableBitmap);
return workingBitmap.copy(Bitmap.Config.ARGB_8888, true);
}
I found above method inside here. I have tested using both the methods in my application, and it works perfectly after I added above tweaks. Try it and let me know if it works or not.
use framelayout. put two imageviews inside the framelayout and specify the position of the watermark imageview.
I'm trying to draw a text on the center of a bitmap however I can't do it even though I used align.center. The code is:
public Bitmap drawTextToBitmap(Context gContext, String gText) {
Resources resources = gContext.getResources();
float scale = resources.getDisplayMetrics().density;
Bitmap bitmap =
BitmapFactory.decodeResource(resources, R.drawable.blank_marker);
android.graphics.Bitmap.Config bitmapConfig =
bitmap.getConfig();
// set default bitmap config if none
if(bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
// resource bitmaps are imutable,
// so we need to convert it to mutable one
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
// new antialised Paint
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// text color - #3D3D3D
paint.setColor(Color.rgb(61, 61, 61));
// text size in pixels
paint.setTextSize((int) (25 * scale));
// text shadow
paint.setShadowLayer(1f, 0f, 1f, Color.WHITE);
// draw text to the Canvas center
Rect bounds = new Rect();
paint.setTextAlign(Align.CENTER);
paint.getTextBounds(gText, 0, gText.length(), bounds);
int x = (bitmap.getWidth() - bounds.width())/2;
int y = (bitmap.getHeight() + bounds.height())/2;
canvas.drawText(gText, x * scale, y * scale, paint);
return bitmap;
}
What am I doing wrong?
It's a lot more straightforward than you think.
Draw the text at half the Bitmap's width and height (center point) in combination with Paint.setTextAlign(Align.CENTER).
The alignment property will take care of the rest.
I guess none of the answers given above are good enough so I post my answer. Try it out guys, it will work on all devices and is not complex at all:
String text = "Text"; //your string
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(activity.getResources().getColor(R.color.white));
paint.setTextSize(30);
// draw text to the Canvas center
Rect boundsText = new Rect();
paint.getTextBounds(text, 0, text.length(), boundsText);
int x = (bitmap.getWidth() - boundsText.width()) / 2;
int y = (bitmap.getHeight() + boundsText.height()) / 2;
canvas.drawText(text, x, y, paint);
Where is the text drawing? The issue might be since you changed the text align to Align.CENTER. Your code calculating x and y assumes the text rendering is using Align.LEFT, I believe.
Either use setTextAlign(Align.CENTER) and render at the actual bitmap center, or use setTextAlign(Align.LEFT) and use the current x and y calculations you are using.
For example I want a white border of 10pixel around all 4 side of the bitmap. I am not using it for imageview
I am currently using this code to crop image. May I know how I could add a white border into it?
public Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// Compute the scaling factors to fit the new height and width, respectively.
// To cover the final image, the final scaling will be the bigger
// of these two.
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
// Now get the size of the source bitmap when scaled
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
// Let's find out the upper left coordinates if the scaled bitmap
// should be centered in the new size give by the parameters
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
// The target rectangle for the new, scaled version of the source bitmap will now
// be
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
// Finally, we create a new bitmap of the specified size and draw our new,
// scaled bitmap onto it.
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
I wrote a function for this:
private Bitmap addWhiteBorder(Bitmap bmp, int borderSize) {
Bitmap bmpWithBorder = Bitmap.createBitmap(bmp.getWidth() + borderSize * 2, bmp.getHeight() + borderSize * 2, bmp.getConfig());
Canvas canvas = new Canvas(bmpWithBorder);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(bmp, borderSize, borderSize, null);
return bmpWithBorder;
}
Basically it creates a new Bitmap adding 2 * bordersize to each dimension and then paints the original Bitmap over it, offsetting it with bordersize.
As for a way of doing this. You make your bitmap bigger than the one your adding to it and then fill the canvas with the background you want. If you need to add other effects you can look into the canvas options for clipping the rect and adding rounded corners and such.
RectF targetRect = new RectF(left+10, top+10, left + scaledWidth, top + scaledHeight);
Bitmap dest = Bitmap.createBitmap(newWidth+20, newHeight+20, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(source, null, targetRect, null);
You can draw 4 rectangles after painting your bitmap's stuff.
point 0,0,3,sizey
point 0,0,sizex,3
point 0,sizey-3,sizex,sizey
point sizex-3,0,sizex,sizey
the accepted answer is nice but in the cases that bitmap contains a transparent background, it fills all over the background of source bitmap with white pixels. so it doesn't work fine for all cases.
a better way to achieve this goal is using Canvas#drawLine method like the following code:
Bitmap drawBorder(Bitmap source) {
int width = source.getWidth();
int height = source.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, source.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setStrokeWidth(50);
paint.setColor(Color.WHITE);
canvas.drawLine(0, 0, width, 0, paint);
canvas.drawLine(width, 0, width, height, paint);
canvas.drawLine(width, height, 0, height, paint);
canvas.drawLine(0, height, 0, 0, paint);
canvas.drawBitmap(source, 0, 0, null);
return bitmap;
}
in this way we first create a second bitmap using source bitmap width, height and config and use drawline() mathod four times to draw four lines using coordinates of end points of each line around the second bitmap and then draw the source bitmap on the second bitmap that must be returned.
A super easy way of doing it would be to set the ImageView background to white and add a padding value.
If that doesn't work, create a FrameLayout with w/h of wrap_content, set its background to white, put the ImageView in there, and set the ImageView's margins to the desired border width.
Its not elegant but you can always just draw a rectangle behind it, you already have the code to do this and any performance impact is going to be unnoticeable
You can create your targetRectangle 20px wider and 20px higher
RectF targetRect = new RectF(left, top, left + scaledWidth + 20, top + scaledHeight + 20);
and paint the background white
Try this it will also add border to your canvas
canvas.drawLine(0, 0, canvas.getWidth(), 0, paint2);
canvas.drawLine(0, 0, 0, canvas.getHeight(), paint2);
canvas.drawLine(0, canvas.getHeight(), canvas.getWidth(),
canvas.getHeight(), paint2);
canvas.drawLine(canvas.getWidth(), 0, canvas.getWidth(),
canvas.getHeight(), paint2);